b85a4273985806df02a0f57d3169888f31b8f651
[WebKit-https.git] / WebCore / bindings / js / kjs_window.cpp
1 // -*- c-basic-offset: 4 -*-
2 /*
3  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
5  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reseved.
6  *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "kjs_window.h"
25
26 #include "Base64.h"
27 #include "CString.h"
28 #include "Chrome.h"
29 #include "DOMWindow.h"
30 #include "Element.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "ExceptionCode.h"
34 #include "FloatRect.h"
35 #include "Frame.h"
36 #include "FrameLoadRequest.h"
37 #include "FrameLoader.h"
38 #include "FrameTree.h"
39 #include "FrameView.h"
40 #include "GCController.h"
41 #include "HTMLDocument.h"
42 #include "JSCSSRule.h"
43 #include "JSCSSValue.h"
44 #include "JSDOMExceptionConstructor.h"
45 #include "JSDOMWindow.h"
46 #include "JSEvent.h"
47 #include "JSHTMLCollection.h"
48 #include "JSHTMLOptionElementConstructor.h"
49 #include "JSMutationEvent.h"
50 #include "JSNode.h"
51 #include "JSNodeFilter.h"
52 #include "JSRange.h"
53 #include "JSXMLHttpRequest.h"
54 #include "Logging.h"
55 #include "Page.h"
56 #include "PlatformScreen.h"
57 #include "PlugInInfoStore.h"
58 #include "RenderView.h"
59 #include "Settings.h"
60 #include "WindowFeatures.h"
61 #include "htmlediting.h"
62 #include "kjs_css.h"
63 #include "kjs_events.h"
64 #include "kjs_navigator.h"
65 #include "kjs_proxy.h"
66 #include <wtf/MathExtras.h>
67
68 #if ENABLE(XSLT)
69 #include "JSXSLTProcessor.h"
70 #endif
71
72 using namespace WebCore;
73 using namespace EventNames;
74
75 namespace KJS {
76
77 static int lastUsedTimeoutId;
78
79 static int timerNestingLevel = 0;
80 const int cMaxTimerNestingLevel = 5;
81 const double cMinimumTimerInterval = 0.010;
82
83 struct WindowPrivate {
84     WindowPrivate() 
85         : loc(0)
86         , m_evt(0)
87         , m_returnValueSlot(0)
88     {
89     }
90
91     Window::ListenersMap jsEventListeners;
92     Window::ListenersMap jsHTMLEventListeners;
93     Window::UnprotectedListenersMap jsUnprotectedEventListeners;
94     Window::UnprotectedListenersMap jsUnprotectedHTMLEventListeners;
95     mutable Location* loc;
96     WebCore::Event *m_evt;
97     JSValue **m_returnValueSlot;
98     typedef HashMap<int, DOMWindowTimer*> TimeoutsMap;
99     TimeoutsMap m_timeouts;
100 };
101
102 class DOMWindowTimer : public TimerBase {
103 public:
104     DOMWindowTimer(int timeoutId, int nestingLevel, Window* o, ScheduledAction* a)
105         : m_timeoutId(timeoutId), m_nestingLevel(nestingLevel), m_object(o), m_action(a) { }
106     virtual ~DOMWindowTimer() 
107     { 
108         JSLock lock;
109         delete m_action; 
110     }
111
112     int timeoutId() const { return m_timeoutId; }
113     
114     int nestingLevel() const { return m_nestingLevel; }
115     void setNestingLevel(int n) { m_nestingLevel = n; }
116     
117     ScheduledAction* action() const { return m_action; }
118     ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
119
120 private:
121     virtual void fired();
122
123     int m_timeoutId;
124     int m_nestingLevel;
125     Window* m_object;
126     ScheduledAction* m_action;
127 };
128
129 class PausedTimeout {
130 public:
131     int timeoutId;
132     int nestingLevel;
133     double nextFireInterval;
134     double repeatInterval;
135     ScheduledAction *action;
136 };
137
138 } // namespace KJS
139
140 #include "kjs_window.lut.h"
141
142 namespace KJS {
143
144 ////////////////////// Window Object ////////////////////////
145
146 const ClassInfo Window::info = { "Window", 0, &WindowTable, 0 };
147
148 /*
149 @begin WindowTable 118
150 # Warning, when adding a function to this object you need to add a case in Window::get
151 # -- Functions --
152   atob                  Window::AToB                DontDelete|Function 1
153   btoa                  Window::BToA                DontDelete|Function 1
154   scroll                Window::Scroll              DontDelete|Function 2
155   scrollBy              Window::ScrollBy            DontDelete|Function 2
156   scrollTo              Window::ScrollTo            DontDelete|Function 2
157   moveBy                Window::MoveBy              DontDelete|Function 2
158   moveTo                Window::MoveTo              DontDelete|Function 2
159   resizeBy              Window::ResizeBy            DontDelete|Function 2
160   resizeTo              Window::ResizeTo            DontDelete|Function 2
161   open                  Window::Open                DontDelete|Function 3
162   setTimeout            Window::SetTimeout          DontDelete|Function 2
163   clearTimeout          Window::ClearTimeout        DontDelete|Function 1
164   setInterval           Window::SetInterval         DontDelete|Function 2
165   clearInterval         Window::ClearInterval       DontDelete|Function 1
166   captureEvents         Window::CaptureEvents       DontDelete|Function 0
167   releaseEvents         Window::ReleaseEvents       DontDelete|Function 0
168   addEventListener      Window::AddEventListener    DontDelete|Function 3
169   removeEventListener   Window::RemoveEventListener DontDelete|Function 3
170   showModalDialog       Window::ShowModalDialog     DontDelete|Function 1
171 # -- Attributes --
172   crypto                Window::Crypto              DontDelete|ReadOnly
173   event                 Window::Event_              DontDelete
174   location              Window::Location_           DontDelete
175   navigator             Window::Navigator_          DontDelete|ReadOnly
176   clientInformation     Window::ClientInformation   DontDelete|ReadOnly
177 # -- Event Listeners --
178   onabort               Window::Onabort             DontDelete
179   onblur                Window::Onblur              DontDelete
180   onchange              Window::Onchange            DontDelete
181   onclick               Window::Onclick             DontDelete
182   ondblclick            Window::Ondblclick          DontDelete
183   onerror               Window::Onerror             DontDelete
184   onfocus               Window::Onfocus             DontDelete
185   onkeydown             Window::Onkeydown           DontDelete
186   onkeypress            Window::Onkeypress          DontDelete
187   onkeyup               Window::Onkeyup             DontDelete
188   onload                Window::Onload              DontDelete
189   onmousedown           Window::Onmousedown         DontDelete
190   onmousemove           Window::Onmousemove         DontDelete
191   onmouseout            Window::Onmouseout          DontDelete
192   onmouseover           Window::Onmouseover         DontDelete
193   onmouseup             Window::Onmouseup           DontDelete
194   onmousewheel          Window::OnWindowMouseWheel  DontDelete
195   onreset               Window::Onreset             DontDelete
196   onresize              Window::Onresize            DontDelete
197   onscroll              Window::Onscroll            DontDelete
198   onsearch              Window::Onsearch            DontDelete
199   onselect              Window::Onselect            DontDelete
200   onsubmit              Window::Onsubmit            DontDelete
201   onunload              Window::Onunload            DontDelete
202   onbeforeunload        Window::Onbeforeunload      DontDelete
203 # -- Constructors --
204   DOMException          Window::DOMException        DontDelete
205   Image                 Window::Image               DontDelete
206   Option                Window::Option              DontDelete
207   XMLHttpRequest        Window::XMLHttpRequest      DontDelete
208   XSLTProcessor         Window::XSLTProcessor_      DontDelete
209 @end
210 */
211
212 Window::Window(DOMWindow* window)
213   : m_impl(window)
214   , d(new WindowPrivate)
215 {
216 }
217
218 Window::~Window()
219 {
220     clearAllTimeouts();
221
222     // Clear any backpointers to the window
223
224     ListenersMap::iterator i2 = d->jsEventListeners.begin();
225     ListenersMap::iterator e2 = d->jsEventListeners.end();
226     for (; i2 != e2; ++i2)
227         i2->second->clearWindowObj();
228     i2 = d->jsHTMLEventListeners.begin();
229     e2 = d->jsHTMLEventListeners.end();
230     for (; i2 != e2; ++i2)
231         i2->second->clearWindowObj();
232
233     UnprotectedListenersMap::iterator i1 = d->jsUnprotectedEventListeners.begin();
234     UnprotectedListenersMap::iterator e1 = d->jsUnprotectedEventListeners.end();
235     for (; i1 != e1; ++i1)
236         i1->second->clearWindowObj();
237     i1 = d->jsUnprotectedHTMLEventListeners.begin();
238     e1 = d->jsUnprotectedHTMLEventListeners.end();
239     for (; i1 != e1; ++i1)
240         i1->second->clearWindowObj();
241 }
242
243 ScriptInterpreter* Window::interpreter() const
244 {
245     Frame* frame = impl()->frame();
246     if (!frame)
247         return 0;
248
249     return frame->scriptProxy()->interpreter();
250 }
251
252 Window *Window::retrieveWindow(Frame *f)
253 {
254     JSObject *o = retrieve(f)->getObject();
255
256     ASSERT(o || !f->settings() || !f->settings()->isJavaScriptEnabled());
257     return static_cast<Window *>(o);
258 }
259
260 Window *Window::retrieveActive(ExecState *exec)
261 {
262     JSValue *imp = exec->dynamicInterpreter()->globalObject();
263     ASSERT(imp);
264     return static_cast<Window*>(imp);
265 }
266
267 JSValue *Window::retrieve(Frame *p)
268 {
269     ASSERT(p);
270     if (KJSProxy *proxy = p->scriptProxy())
271         return proxy->interpreter()->globalObject(); // the Global object is the "window"
272   
273     return jsUndefined(); // This can happen with JS disabled on the domain of that window
274 }
275
276 Location *Window::location() const
277 {
278   if (!d->loc)
279     d->loc = new Location(impl()->frame());
280   return d->loc;
281 }
282
283 // reference our special objects during garbage collection
284 void Window::mark()
285 {
286   JSObject::mark();
287   if (d->loc && !d->loc->marked())
288     d->loc->mark();
289 }
290
291 static bool allowPopUp(ExecState *exec, Window *window)
292 {
293     Frame* frame = window->impl()->frame();
294     if (!frame)
295         return false;
296     if (static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->wasRunByUserGesture())
297         return true;
298     Settings* settings = frame->settings();
299     return settings && settings->JavaScriptCanOpenWindowsAutomatically();
300 }
301
302 static HashMap<String, String> parseModalDialogFeatures(ExecState *exec, JSValue *featuresArg)
303 {
304     HashMap<String, String> map;
305
306     Vector<String> features = valueToStringWithUndefinedOrNullCheck(exec, featuresArg).split(';');
307     Vector<String>::const_iterator end = features.end();
308     for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
309         String s = *it;
310         int pos = s.find('=');
311         int colonPos = s.find(':');
312         if (pos >= 0 && colonPos >= 0)
313             continue; // ignore any strings that have both = and :
314         if (pos < 0)
315             pos = colonPos;
316         if (pos < 0) {
317             // null string for value means key without value
318             map.set(s.stripWhiteSpace().lower(), String());
319         } else {
320             String key = s.left(pos).stripWhiteSpace().lower();
321             String val = s.substring(pos + 1).stripWhiteSpace().lower();
322             int spacePos = val.find(' ');
323             if (spacePos != -1)
324                 val = val.left(spacePos);
325             map.set(key, val);
326         }
327     }
328
329     return map;
330 }
331
332 static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false)
333 {
334     HashMap<String, String>::const_iterator it = features.find(key);
335     if (it == features.end())
336         return defaultValue;
337     const String& value = it->second;
338     return value.isNull() || value == "1" || value == "yes" || value == "on";
339 }
340
341 static float floatFeature(const HashMap<String, String> &features, const char *key, float min, float max, float defaultValue)
342 {
343     HashMap<String, String>::const_iterator it = features.find(key);
344     if (it == features.end())
345         return defaultValue;
346     // FIXME: Can't distinguish "0q" from string with no digits in it -- both return d == 0 and ok == false.
347     // Would be good to tell them apart somehow since string with no digits should be default value and
348     // "0q" should be minimum value.
349     bool ok;
350     double d = it->second.toDouble(&ok);
351     if ((d == 0 && !ok) || isnan(d))
352         return defaultValue;
353     if (d < min || max <= min)
354         return min;
355     if (d > max)
356         return max;
357     return static_cast<int>(d);
358 }
359
360 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
361     const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
362 {
363     Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
364     
365     ResourceRequest request;
366     if (activeFrame)
367         request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
368     FrameLoadRequest frameRequest(request, frameName);
369
370     // FIXME: It's much better for client API if a new window starts with a URL, here where we
371     // know what URL we are going to open. Unfortunately, this code passes the empty string
372     // for the URL, but there's a reason for that. Before loading we have to set up the opener,
373     // openedByJS, and dialogArguments values. Also, to decide whether to use the URL we currently
374     // do an isSafeScript call using the window we create, which can't be done before creating it.
375     // We'd have to resolve all those issues to pass the URL instead of "".
376
377     bool created;
378     Frame* newFrame = openerFrame->loader()->createWindow(frameRequest, windowFeatures, created);
379     if (!newFrame)
380         return 0;
381
382     newFrame->loader()->setOpener(openerFrame);
383     newFrame->loader()->setOpenedByDOM();
384
385     Window* newWindow = Window::retrieveWindow(newFrame);    
386     
387     if (dialogArgs)
388         newWindow->putDirect("dialogArguments", dialogArgs);
389
390     if (!url.startsWith("javascript:", false) || newWindow->isSafeScript(exec)) {
391         String completedURL = url.isEmpty() ? url : activeFrame->document()->completeURL(url);
392         bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
393         
394         if (created) {
395             newFrame->loader()->changeLocation(KURL(completedURL.deprecatedString()), activeFrame->loader()->outgoingReferrer(), false, userGesture);
396             if (Document* oldDoc = openerFrame->document()) {
397                 newFrame->document()->setDomain(oldDoc->domain(), true);
398                 newFrame->document()->setBaseURL(oldDoc->baseURL());
399             }
400         } else if (!url.isEmpty())
401             newFrame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
402     }
403     
404     return newFrame;
405 }
406
407 static bool canShowModalDialog(const Window *window)
408 {
409     Frame* frame = window->impl()->frame();
410     if (!frame)
411         return false;
412
413     return frame->page()->chrome()->canRunModal();
414 }
415
416 static bool canShowModalDialogNow(const Window *window)
417 {
418     Frame* frame = window->impl()->frame();
419     if (!frame)
420         return false;
421
422     return frame->page()->chrome()->canRunModalNow();
423 }
424
425 static JSValue* showModalDialog(ExecState* exec, Window* openerWindow, const List& args)
426 {
427     if (!canShowModalDialogNow(openerWindow) || !allowPopUp(exec, openerWindow))
428         return jsUndefined();
429
430     const HashMap<String, String> features = parseModalDialogFeatures(exec, args[2]);
431
432     bool trusted = false;
433
434     WindowFeatures wargs;
435
436     // The following features from Microsoft's documentation are not implemented:
437     // - default font settings
438     // - width, height, left, and top specified in units other than "px"
439     // - edge (sunken or raised, default is raised)
440     // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
441     // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
442     // - unadorned: trusted && boolFeature(features, "unadorned");
443     Frame* frame = openerWindow->impl()->frame();
444     if (!frame)
445         return jsUndefined();
446
447     FloatRect screenRect = screenAvailableRect(frame->view());
448
449     wargs.width = floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
450     wargs.widthSet = true;
451     wargs.height = floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
452     wargs.heightSet = true;
453
454     wargs.x = floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
455     wargs.xSet = wargs.x > 0;
456     wargs.y = floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
457     wargs.ySet = wargs.y > 0;
458
459     if (boolFeature(features, "center", true)) {
460         if (!wargs.xSet) {
461             wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
462             wargs.xSet = true;
463         }
464         if (!wargs.ySet) {
465             wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
466             wargs.ySet = true;
467         }
468     }
469
470     wargs.dialog = true;
471     wargs.resizable = boolFeature(features, "resizable");
472     wargs.scrollbarsVisible = boolFeature(features, "scroll", true);
473     wargs.statusBarVisible = boolFeature(features, "status", !trusted);
474     wargs.menuBarVisible = false;
475     wargs.toolBarVisible = false;
476     wargs.locationBarVisible = false;
477     wargs.fullscreen = false;
478     
479     Frame* dialogFrame = createWindow(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args[0]), "", wargs, args[1]);
480     if (!dialogFrame)
481         return jsUndefined();
482
483     Window* dialogWindow = Window::retrieveWindow(dialogFrame);
484
485     // Get the return value either just before clearing the dialog window's
486     // properties (in Window::clear), or when on return from runModal.
487     JSValue* returnValue = 0;
488     dialogWindow->setReturnValueSlot(&returnValue);
489     dialogFrame->page()->chrome()->runModal();
490     dialogWindow->setReturnValueSlot(0);
491
492     // If we don't have a return value, get it now.
493     // Either Window::clear was not called yet, or there was no return value,
494     // and in that case, there's no harm in trying again (no benefit either).
495     if (!returnValue)
496         returnValue = dialogWindow->getDirect("returnValue");
497
498     return returnValue ? returnValue : jsUndefined();
499 }
500
501 JSValue *Window::getValueProperty(ExecState *exec, int token) const
502 {
503    ASSERT(impl()->frame());
504
505    switch (token) {
506    case Crypto:
507       if (!isSafeScript(exec))
508         return jsUndefined();
509       return jsUndefined(); // FIXME: implement this
510    case DOMException:
511       if (!isSafeScript(exec))
512         return jsUndefined();
513       return getDOMExceptionConstructor(exec);
514     case Event_:
515       if (!isSafeScript(exec))
516         return jsUndefined();
517       if (!d->m_evt)
518         return jsUndefined();
519       return toJS(exec, d->m_evt);
520     case Location_:
521       return location();
522     case Navigator_:
523     case ClientInformation: {
524       if (!isSafeScript(exec))
525         return jsUndefined();
526       // Store the navigator in the object so we get the same one each time.
527       Navigator *n = new Navigator(exec, impl()->frame());
528       // FIXME: this will make the "navigator" object accessible from windows that fail
529       // the security check the first time, but not subsequent times, seems weird.
530       const_cast<Window *>(this)->putDirect("navigator", n, DontDelete|ReadOnly);
531       const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete|ReadOnly);
532       return n;
533     }
534     case Image:
535       if (!isSafeScript(exec))
536         return jsUndefined();
537       // FIXME: this property (and the few below) probably shouldn't create a new object every
538       // time
539       return new ImageConstructorImp(exec, impl()->frame()->document());
540     case Option:
541       if (!isSafeScript(exec))
542         return jsUndefined();
543       return new JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
544     case XMLHttpRequest:
545       if (!isSafeScript(exec))
546         return jsUndefined();
547       return new JSXMLHttpRequestConstructorImp(exec, impl()->frame()->document());
548 #if ENABLE(XSLT)
549     case XSLTProcessor_:
550       if (!isSafeScript(exec))
551         return jsUndefined();
552       return new XSLTProcessorConstructorImp(exec);
553 #else
554     case XSLTProcessor_:
555       return jsUndefined();
556 #endif
557    }
558
559    if (!isSafeScript(exec))
560      return jsUndefined();
561
562    switch (token) {
563    case Onabort:
564      return getListener(exec, abortEvent);
565    case Onblur:
566      return getListener(exec, blurEvent);
567    case Onchange:
568      return getListener(exec, changeEvent);
569    case Onclick:
570      return getListener(exec, clickEvent);
571    case Ondblclick:
572      return getListener(exec, dblclickEvent);
573    case Onerror:
574      return getListener(exec, errorEvent);
575    case Onfocus:
576      return getListener(exec, focusEvent);
577    case Onkeydown:
578      return getListener(exec, keydownEvent);
579    case Onkeypress:
580      return getListener(exec, keypressEvent);
581    case Onkeyup:
582      return getListener(exec, keyupEvent);
583    case Onload:
584      return getListener(exec, loadEvent);
585    case Onmousedown:
586      return getListener(exec, mousedownEvent);
587    case Onmousemove:
588      return getListener(exec, mousemoveEvent);
589    case Onmouseout:
590      return getListener(exec, mouseoutEvent);
591    case Onmouseover:
592      return getListener(exec, mouseoverEvent);
593    case Onmouseup:
594      return getListener(exec, mouseupEvent);
595    case OnWindowMouseWheel:
596      return getListener(exec, mousewheelEvent);
597    case Onreset:
598      return getListener(exec, resetEvent);
599    case Onresize:
600      return getListener(exec,resizeEvent);
601    case Onscroll:
602      return getListener(exec,scrollEvent);
603    case Onsearch:
604      return getListener(exec,searchEvent);
605    case Onselect:
606      return getListener(exec,selectEvent);
607    case Onsubmit:
608      return getListener(exec,submitEvent);
609    case Onbeforeunload:
610       return getListener(exec, beforeunloadEvent);
611     case Onunload:
612      return getListener(exec, unloadEvent);
613    }
614    ASSERT(0);
615    return jsUndefined();
616 }
617
618 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
619 {
620     return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName)));
621 }
622
623 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
624 {
625     return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index()));
626 }
627
628 JSValue *Window::namedItemGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
629 {
630   Window *thisObj = static_cast<Window *>(slot.slotBase());
631   Document *doc = thisObj->impl()->frame()->document();
632   ASSERT(thisObj->isSafeScript(exec) && doc && doc->isHTMLDocument());
633
634   String name = propertyName;
635   RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(name);
636   if (collection->length() == 1)
637     return toJS(exec, collection->firstItem());
638   return toJS(exec, collection.get());
639 }
640
641 bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
642 {
643   // Check for child frames by name before built-in properties to
644   // match Mozilla. This does not match IE, but some sites end up
645   // naming frames things that conflict with window properties that
646   // are in Moz but not IE. Since we have some of these, we have to do
647   // it the Moz way.
648   if (impl()->frame()->tree()->child(propertyName)) {
649     slot.setCustom(this, childFrameGetter);
650     return true;
651   }
652
653   const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
654   if (entry) {
655     if (entry->attr & Function) {
656       switch (entry->value) {
657       case ShowModalDialog:
658         if (!canShowModalDialog(this))
659           return false;
660         // fall through
661       default:
662         if (isSafeScript(exec))
663           slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
664         else
665           slot.setUndefined(this);
666       } 
667     } else
668       slot.setStaticEntry(this, entry, staticValueGetter<Window>);
669     return true;
670   }
671
672   // FIXME: Search the whole frame hierachy somewhere around here.
673   // We need to test the correct priority order.
674   
675   // allow window[1] or parent[1] etc. (#56983)
676   bool ok;
677   unsigned i = propertyName.toArrayIndex(&ok);
678   if (ok && i < impl()->frame()->tree()->childCount()) {
679     slot.setCustomIndex(this, i, indexGetter);
680     return true;
681   }
682
683   // allow shortcuts like 'Image1' instead of document.images.Image1
684   Document* doc = impl()->frame()->document();
685   if (doc && doc->isHTMLDocument()) {
686     if (!isSafeScript(exec)) {
687       slot.setUndefined(this);
688       return true;
689     }
690
691     AtomicString atomicPropertyName = propertyName;
692     if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
693       slot.setCustom(this, namedItemGetter);
694       return true;
695     }
696   }
697
698   if (!isSafeScript(exec)) {
699     slot.setUndefined(this);
700     return true;
701   }
702
703   return JSObject::getOwnPropertySlot(exec, propertyName, slot);
704 }
705
706 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
707 {
708   const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
709   if (entry) {
710      if (entry->attr & Function) {
711        if (isSafeScript(exec))
712          JSObject::put(exec, propertyName, value, attr);
713        return;
714     }
715     if (entry->attr & ReadOnly)
716       return;
717
718     switch (entry->value) {
719     case Location_: {
720       Frame* p = Window::retrieveActive(exec)->impl()->frame();
721       if (p) {
722         DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(value->toString(exec))).url();
723         if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
724           bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
725           // We want a new history item if this JS was called via a user gesture
726           impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
727         }
728       }
729       return;
730     }
731     case Onabort:
732       if (isSafeScript(exec))
733         setListener(exec, abortEvent,value);
734       return;
735     case Onblur:
736       if (isSafeScript(exec))
737         setListener(exec, blurEvent,value);
738       return;
739     case Onchange:
740       if (isSafeScript(exec))
741         setListener(exec, changeEvent,value);
742       return;
743     case Onclick:
744       if (isSafeScript(exec))
745         setListener(exec,clickEvent,value);
746       return;
747     case Ondblclick:
748       if (isSafeScript(exec))
749         setListener(exec, dblclickEvent,value);
750       return;
751     case Onerror:
752       if (isSafeScript(exec))
753         setListener(exec, errorEvent, value);
754       return;
755     case Onfocus:
756       if (isSafeScript(exec))
757         setListener(exec,focusEvent,value);
758       return;
759     case Onkeydown:
760       if (isSafeScript(exec))
761         setListener(exec,keydownEvent,value);
762       return;
763     case Onkeypress:
764       if (isSafeScript(exec))
765         setListener(exec,keypressEvent,value);
766       return;
767     case Onkeyup:
768       if (isSafeScript(exec))
769         setListener(exec,keyupEvent,value);
770       return;
771     case Onload:
772       if (isSafeScript(exec))
773         setListener(exec,loadEvent,value);
774       return;
775     case Onmousedown:
776       if (isSafeScript(exec))
777         setListener(exec,mousedownEvent,value);
778       return;
779     case Onmousemove:
780       if (isSafeScript(exec))
781         setListener(exec,mousemoveEvent,value);
782       return;
783     case Onmouseout:
784       if (isSafeScript(exec))
785         setListener(exec,mouseoutEvent,value);
786       return;
787     case Onmouseover:
788       if (isSafeScript(exec))
789         setListener(exec,mouseoverEvent,value);
790       return;
791     case Onmouseup:
792       if (isSafeScript(exec))
793         setListener(exec,mouseupEvent,value);
794       return;
795     case OnWindowMouseWheel:
796       if (isSafeScript(exec))
797         setListener(exec, mousewheelEvent,value);
798       return;
799     case Onreset:
800       if (isSafeScript(exec))
801         setListener(exec,resetEvent,value);
802       return;
803     case Onresize:
804       if (isSafeScript(exec))
805         setListener(exec,resizeEvent,value);
806       return;
807     case Onscroll:
808       if (isSafeScript(exec))
809         setListener(exec,scrollEvent,value);
810       return;
811     case Onsearch:
812         if (isSafeScript(exec))
813             setListener(exec,searchEvent,value);
814         return;
815     case Onselect:
816       if (isSafeScript(exec))
817         setListener(exec,selectEvent,value);
818       return;
819     case Onsubmit:
820       if (isSafeScript(exec))
821         setListener(exec,submitEvent,value);
822       return;
823     case Onbeforeunload:
824       if (isSafeScript(exec))
825         setListener(exec, beforeunloadEvent, value);
826       return;
827     case Onunload:
828       if (isSafeScript(exec))
829         setListener(exec, unloadEvent, value);
830       return;
831     default:
832       break;
833     }
834   }
835   if (isSafeScript(exec))
836     JSObject::put(exec, propertyName, value, attr);
837 }
838
839 static bool shouldLoadAsEmptyDocument(const KURL &url)
840 {
841   return url.protocol().lower() == "about" || url.isEmpty();
842 }
843
844 bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
845 {
846     if (origin == target)
847         return true;
848         
849     Frame* originFrame = origin->frame();
850     Frame* targetFrame = target->frame();
851
852     // JS may be attempting to access the "window" object, which should be valid,
853     // even if the document hasn't been constructed yet.  If the document doesn't
854     // exist yet allow JS to access the window object.
855     if (!targetFrame->document())
856         return true;
857
858     WebCore::Document *originDocument = originFrame->document();
859     WebCore::Document *targetDocument = targetFrame->document();
860
861     if (!targetDocument) {
862         return false;
863     }
864
865     WebCore::String targetDomain = targetDocument->domain();
866
867     // Always allow local pages to execute any JS.
868     if (targetDomain.isNull())
869         return true;
870
871     WebCore::String originDomain = originDocument->domain();
872
873     // if this document is being initially loaded as empty by its parent
874     // or opener, allow access from any document in the same domain as
875     // the parent or opener.
876     if (shouldLoadAsEmptyDocument(targetFrame->loader()->url())) {
877         Frame* ancestorFrame = targetFrame->loader()->opener() ? targetFrame->loader()->opener() : targetFrame->tree()->parent();
878         while (ancestorFrame && shouldLoadAsEmptyDocument(ancestorFrame->loader()->url()))
879             ancestorFrame = ancestorFrame->tree()->parent();
880         if (ancestorFrame)
881             originDomain = ancestorFrame->document()->domain();
882     }
883
884     if ( targetDomain == originDomain )
885         return true;
886
887     if (Interpreter::shouldPrintExceptions()) {
888         printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
889              targetDocument->URL().latin1(), originDocument->URL().latin1());
890     }
891     String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
892                   targetDocument->URL().latin1(), originDocument->URL().latin1());
893     if (Page* page = targetFrame->page())
894         page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
895
896     return false;
897 }
898
899 bool Window::isSafeScript(ExecState *exec) const
900 {
901   Frame* frame = impl()->frame();
902   if (!frame) // frame deleted ? can't grant access
903     return false;
904   Frame* activeFrame = static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->frame();
905   if (!activeFrame)
906     return false;
907   if (activeFrame == frame) // Not calling from another frame, no problem.
908     return true;
909
910   // JS may be attempting to access the "window" object, which should be valid,
911   // even if the document hasn't been constructed yet.  If the document doesn't
912   // exist yet allow JS to access the window object.
913   if (!frame->document())
914       return true;
915
916   WebCore::Document* thisDocument = frame->document();
917   WebCore::Document* actDocument = activeFrame->document();
918
919   KURL actURL = activeFrame->loader()->url();
920   WebCore::String actDomain = actDocument ? actDocument->domain() : actURL.host();
921   
922   // FIXME: this really should be explicitly checking for the "file:" protocol instead
923   // Always allow local pages to execute any JS.
924   if (actDomain.isEmpty())
925     return true;
926
927   KURL thisURL = frame->loader()->url();
928   WebCore::String thisDomain = thisDocument->domain();
929
930   // If this document is being initially loaded as empty by its parent
931   // or opener, allow access from any document in the same domain as
932   // the parent or opener.
933   if (shouldLoadAsEmptyDocument(thisURL)) {
934     Frame* ancestorFrame = frame->loader()->opener() ? frame->loader()->opener() : frame->tree()->parent();
935     while (ancestorFrame && shouldLoadAsEmptyDocument(ancestorFrame->loader()->url()))
936       ancestorFrame = ancestorFrame->tree()->parent();
937     if (ancestorFrame) {
938       thisDomain = ancestorFrame->document()->domain();
939       thisURL = ancestorFrame->loader()->url();
940     }
941   }
942
943   if (actDomain == thisDomain && actURL.protocol() == thisURL.protocol() && actURL.port() == thisURL.port())
944     return true;
945
946   if (Interpreter::shouldPrintExceptions()) {
947       printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n", 
948              thisDocument->URL().latin1(), actDocument->URL().latin1());
949   }
950   String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n", 
951                   thisDocument->URL().latin1(), actDocument->URL().latin1());
952   if (Page* page = frame->page())
953       page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
954   
955   return false;
956 }
957
958 void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
959 {
960   if (!isSafeScript(exec))
961     return;
962   Frame* frame = impl()->frame();
963   if (!frame)
964     return;
965   Document* doc = frame->document();
966   if (!doc)
967     return;
968
969   doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func,true));
970 }
971
972 JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
973 {
974   if (!isSafeScript(exec))
975     return jsUndefined();
976   Frame* frame = impl()->frame();
977   if (!frame)
978     return jsUndefined();
979   Document* doc = frame->document();
980   if (!doc)
981     return jsUndefined();
982
983   WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
984   if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
985     return static_cast<JSEventListener*>(listener)->listenerObj();
986   else
987     return jsNull();
988 }
989
990 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
991 {
992     if (!val->isObject())
993         return 0;
994     JSObject* object = static_cast<JSObject*>(val);
995     ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
996     return listeners.get(object);
997 }
998
999 JSEventListener *Window::findOrCreateJSEventListener(JSValue *val, bool html)
1000 {
1001   JSEventListener *listener = findJSEventListener(val, html);
1002   if (listener)
1003     return listener;
1004
1005   if (!val->isObject())
1006     return 0;
1007   JSObject *object = static_cast<JSObject *>(val);
1008
1009   // Note that the JSEventListener constructor adds it to our jsEventListeners list
1010   return new JSEventListener(object, this, html);
1011 }
1012
1013 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1014 {
1015     if (!val->isObject())
1016         return 0;
1017     JSObject* object = static_cast<JSObject*>(val);
1018     UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1019     return listeners.get(object);
1020 }
1021
1022 JSUnprotectedEventListener *Window::findOrCreateJSUnprotectedEventListener(JSValue *val, bool html)
1023 {
1024   JSUnprotectedEventListener *listener = findJSUnprotectedEventListener(val, html);
1025   if (listener)
1026     return listener;
1027
1028   if (!val->isObject())
1029     return 0;
1030   JSObject *object = static_cast<JSObject *>(val);
1031
1032   // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1033   return new JSUnprotectedEventListener(object, this, html);
1034 }
1035
1036 void Window::clearHelperObjectProperties()
1037 {
1038   d->loc = 0;
1039   d->m_evt = 0;
1040 }
1041
1042 void Window::clear()
1043 {
1044   JSLock lock;
1045
1046   if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1047     *d->m_returnValueSlot = getDirect("returnValue");
1048
1049   clearAllTimeouts();
1050   clearProperties();
1051   clearHelperObjectProperties();
1052   setPrototype(JSDOMWindowPrototype::self()); // clear the prototype
1053
1054   // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1055   // disconnected yet
1056   if (Frame* frame = impl()->frame())
1057     frame->scriptProxy()->interpreter()->initGlobalObject();
1058
1059   // there's likely to be lots of garbage now
1060   gcController().garbageCollectSoon();
1061 }
1062
1063 void Window::setCurrentEvent(Event *evt)
1064 {
1065   d->m_evt = evt;
1066 }
1067
1068 static void setWindowFeature(const String& keyString, const String& valueString, WindowFeatures& windowFeatures)
1069 {
1070     int value;
1071     
1072     if (valueString.length() == 0 || // listing a key with no value is shorthand for key=yes
1073         valueString == "yes")
1074         value = 1;
1075     else
1076         value = valueString.toInt();
1077     
1078     if (keyString == "left" || keyString == "screenx") {
1079         windowFeatures.xSet = true;
1080         windowFeatures.x = value;
1081     } else if (keyString == "top" || keyString == "screeny") {
1082         windowFeatures.ySet = true;
1083         windowFeatures.y = value;
1084     } else if (keyString == "width" || keyString == "innerwidth") {
1085         windowFeatures.widthSet = true;
1086         windowFeatures.width = value;
1087     } else if (keyString == "height" || keyString == "innerheight") {
1088         windowFeatures.heightSet = true;
1089         windowFeatures.height = value;
1090     } else if (keyString == "menubar")
1091         windowFeatures.menuBarVisible = value;
1092     else if (keyString == "toolbar")
1093         windowFeatures.toolBarVisible = value;
1094     else if (keyString == "location")
1095         windowFeatures.locationBarVisible = value;
1096     else if (keyString == "status")
1097         windowFeatures.statusBarVisible = value;
1098     else if (keyString == "resizable")
1099         windowFeatures.resizable = value;
1100     else if (keyString == "fullscreen")
1101         windowFeatures.fullscreen = value;
1102     else if (keyString == "scrollbars")
1103         windowFeatures.scrollbarsVisible = value;
1104 }
1105
1106 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
1107 static bool isSeparator(::UChar c)
1108 {
1109     return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
1110 }
1111
1112 static void parseWindowFeatures(const String& features, WindowFeatures& windowFeatures)
1113 {
1114     /*
1115      The IE rule is: all features except for channelmode and fullscreen default to YES, but
1116      if the user specifies a feature string, all features default to NO. (There is no public
1117      standard that applies to this method.)
1118      
1119      <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
1120      */
1121     
1122     windowFeatures.dialog = false;
1123     windowFeatures.fullscreen = false;
1124     
1125     windowFeatures.xSet = false;
1126     windowFeatures.ySet = false;
1127     windowFeatures.widthSet = false;
1128     windowFeatures.heightSet = false;
1129     
1130     if (features.length() == 0) {
1131         windowFeatures.menuBarVisible = true;
1132         windowFeatures.statusBarVisible = true;
1133         windowFeatures.toolBarVisible = true;
1134         windowFeatures.locationBarVisible = true;
1135         windowFeatures.scrollbarsVisible = true;
1136         windowFeatures.resizable = true;
1137         
1138         return;
1139     }
1140     
1141     windowFeatures.menuBarVisible = false;
1142     windowFeatures.statusBarVisible = false;
1143     windowFeatures.toolBarVisible = false;
1144     windowFeatures.locationBarVisible = false;
1145     windowFeatures.scrollbarsVisible = false;
1146     windowFeatures.resizable = false;
1147     
1148     // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
1149     int keyBegin, keyEnd;
1150     int valueBegin, valueEnd;
1151     
1152     int i = 0;
1153     int length = features.length();
1154     String buffer = features.lower();
1155     while (i < length) {
1156         // skip to first non-separator, but don't skip past the end of the string
1157         while (isSeparator(buffer[i])) {
1158             if (i >= length)
1159                 break;
1160             i++;
1161         }
1162         keyBegin = i;
1163         
1164         // skip to first separator
1165         while (!isSeparator(buffer[i]))
1166             i++;
1167         keyEnd = i;
1168         
1169         // skip to first '=', but don't skip past a ',' or the end of the string
1170         while (buffer[i] != '=') {
1171             if (buffer[i] == ',' || i >= length)
1172                 break;
1173             i++;
1174         }
1175         
1176         // skip to first non-separator, but don't skip past a ',' or the end of the string
1177         while (isSeparator(buffer[i])) {
1178             if (buffer[i] == ',' || i >= length)
1179                 break;
1180             i++;
1181         }
1182         valueBegin = i;
1183         
1184         // skip to first separator
1185         while (!isSeparator(buffer[i]))
1186             i++;
1187         valueEnd = i;
1188         
1189         ASSERT(i <= length);
1190
1191         String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
1192         String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
1193         setWindowFeature(keyString, valueString, windowFeatures);
1194     }
1195 }
1196
1197 static void constrainToVisible(const FloatRect& screen, WindowFeatures& windowFeatures)
1198 {
1199     windowFeatures.x += screen.x();
1200     if (windowFeatures.x < screen.x() || windowFeatures.x >= screen.right())
1201         windowFeatures.x = screen.x(); // only safe choice until size is determined
1202     
1203     windowFeatures.y += screen.y();
1204     if (windowFeatures.y < screen.y() || windowFeatures.y >= screen.bottom())
1205         windowFeatures.y = screen.y(); // only safe choice until size is determined
1206     
1207     if (windowFeatures.height > screen.height()) // should actually check workspace
1208         windowFeatures.height = screen.height();
1209     if (windowFeatures.height < 100)
1210         windowFeatures.height = 100;
1211     
1212     if (windowFeatures.width > screen.width()) // should actually check workspace
1213         windowFeatures.width = screen.width();
1214     if (windowFeatures.width < 100)
1215         windowFeatures.width = 100;
1216 }
1217
1218 JSValue *WindowFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
1219 {
1220   if (!thisObj->inherits(&Window::info))
1221     return throwError(exec, TypeError);
1222   Window *window = static_cast<Window *>(thisObj);
1223   Frame *frame = window->impl()->frame();
1224   if (!frame)
1225     return jsUndefined();
1226
1227   FrameView *widget = frame->view();
1228   Page* page = frame->page();
1229   JSValue *v = args[0];
1230   UString s = v->toString(exec);
1231   String str = s;
1232   String str2;
1233
1234   switch (id) {
1235   case Window::AToB:
1236   case Window::BToA: {
1237     if (args.size() < 1)
1238         return throwError(exec, SyntaxError, "Not enough arguments");
1239     if (v->isNull())
1240         return jsString();
1241     if (!s.is8Bit()) {
1242         setDOMException(exec, INVALID_CHARACTER_ERR);
1243         return jsUndefined();
1244     }
1245     
1246     Vector<char> in(s.size());
1247     for (int i = 0; i < s.size(); ++i)
1248         in[i] = static_cast<char>(s.data()[i].unicode());
1249     Vector<char> out;
1250
1251     if (id == Window::AToB) {
1252         if (!base64Decode(in, out))
1253             return throwError(exec, GeneralError, "Cannot decode base64");
1254     } else
1255         base64Encode(in, out);
1256     
1257     return jsString(String(out.data(), out.size()));
1258   }
1259   case Window::Open:
1260   {
1261       String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1262       AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1263
1264       // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1265       // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1266       if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1267           return jsUndefined();
1268       
1269       // Get the target frame for the special cases of _top and _parent
1270       if (frameName == "_top")
1271           while (frame->tree()->parent())
1272                 frame = frame->tree()->parent();
1273       else if (frameName == "_parent")
1274           if (frame->tree()->parent())
1275               frame = frame->tree()->parent();
1276               
1277       // In those cases, we can schedule a location change right now and return early
1278       if (frameName == "_top" || frameName == "_parent") {
1279           String completedURL;
1280           Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1281           if (!urlString.isEmpty() && activeFrame)
1282               completedURL = activeFrame->document()->completeURL(urlString);
1283
1284           const Window* window = Window::retrieveWindow(frame);
1285           if (!completedURL.isEmpty() && (!completedURL.startsWith("javascript:", false) || (window && window->isSafeScript(exec)))) {
1286               bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1287               frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1288           }
1289           return Window::retrieve(frame);
1290       }
1291       
1292       // In the case of a named frame or a new window, we'll use the createWindow() helper
1293       WindowFeatures windowFeatures;
1294       String features = valueToStringWithUndefinedOrNullCheck(exec, args[2]);
1295       parseWindowFeatures(features, windowFeatures);
1296       constrainToVisible(screenRect(page->mainFrame()->view()), windowFeatures);
1297
1298       frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1299
1300       if (!frame)
1301           return jsUndefined();
1302
1303       return Window::retrieve(frame); // global object
1304   }
1305   case Window::ScrollBy:
1306     window->updateLayout();
1307     if(args.size() >= 2 && widget)
1308       widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1309     return jsUndefined();
1310   case Window::Scroll:
1311   case Window::ScrollTo:
1312     window->updateLayout();
1313     if (args.size() >= 2 && widget)
1314       widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1315     return jsUndefined();
1316   case Window::MoveBy:
1317     if (args.size() >= 2 && page) {
1318       FloatRect r = page->chrome()->windowRect();
1319       r.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1320       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1321       if (screenRect(page->mainFrame()->view()).contains(r))
1322         page->chrome()->setWindowRect(r);
1323     }
1324     return jsUndefined();
1325   case Window::MoveTo:
1326     if (args.size() >= 2 && page) {
1327       FloatRect r = page->chrome()->windowRect();
1328       FloatRect sr = screenRect(page->mainFrame()->view());
1329       r.setLocation(sr.location());
1330       r.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1331       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1332       if (sr.contains(r))
1333         page->chrome()->setWindowRect(r);
1334     }
1335     return jsUndefined();
1336   case Window::ResizeBy:
1337     if (args.size() >= 2 && page) {
1338       FloatRect r = page->chrome()->windowRect();
1339       FloatSize dest = r.size() + FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1340       FloatRect sg = screenRect(page->mainFrame()->view());
1341       // Security check: within desktop limits and bigger than 100x100 (per spec)
1342       if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom()
1343            && dest.width() >= 100 && dest.height() >= 100)
1344         page->chrome()->setWindowRect(FloatRect(r.location(), dest));
1345     }
1346     return jsUndefined();
1347   case Window::ResizeTo:
1348     if (args.size() >= 2 && page) {
1349       FloatRect r = page->chrome()->windowRect();
1350       FloatSize dest = FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1351       FloatRect sg = screenRect(page->mainFrame()->view());
1352       // Security check: within desktop limits and bigger than 100x100 (per spec)
1353       if (r.x() + dest.width() <= sg.right() && r.y() + dest.height() <= sg.bottom() &&
1354            dest.width() >= 100 && dest.height() >= 100)
1355         page->chrome()->setWindowRect(FloatRect(r.location(), dest));
1356     }
1357     return jsUndefined();
1358   case Window::SetTimeout:
1359     if (!window->isSafeScript(exec))
1360         return jsUndefined();
1361     if (args.size() >= 2 && v->isString()) {
1362       int i = args[1]->toInt32(exec);
1363       int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1364       return jsNumber(r);
1365     }
1366     else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1367       JSValue *func = args[0];
1368       int i = args[1]->toInt32(exec);
1369
1370       // All arguments after the second should go to the function
1371       // FIXME: could be more efficient
1372       List funcArgs = args.copyTail().copyTail();
1373
1374       int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, true /*single shot*/);
1375       return jsNumber(r);
1376     }
1377     else
1378       return jsUndefined();
1379   case Window::SetInterval:
1380     if (!window->isSafeScript(exec))
1381         return jsUndefined();
1382     if (args.size() >= 2 && v->isString()) {
1383       int i = args[1]->toInt32(exec);
1384       int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1385       return jsNumber(r);
1386     }
1387     else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1388       JSValue *func = args[0];
1389       int i = args[1]->toInt32(exec);
1390
1391       // All arguments after the second should go to the function
1392       // FIXME: could be more efficient
1393       List funcArgs = args.copyTail().copyTail();
1394
1395       int r = (const_cast<Window*>(window))->installTimeout(func, funcArgs, i, false);
1396       return jsNumber(r);
1397     }
1398     else
1399       return jsUndefined();
1400   case Window::ClearTimeout:
1401   case Window::ClearInterval:
1402     if (!window->isSafeScript(exec))
1403         return jsUndefined();
1404     (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1405     return jsUndefined();
1406   case Window::CaptureEvents:
1407   case Window::ReleaseEvents:
1408     // If anyone implements these, they need the safe script security check.
1409     if (!window->isSafeScript(exec))
1410         return jsUndefined();
1411     // Not implemented.
1412     return jsUndefined();
1413   case Window::AddEventListener:
1414         if (!window->isSafeScript(exec))
1415             return jsUndefined();
1416         if (JSEventListener *listener = Window::retrieveActive(exec)->findOrCreateJSEventListener(args[1]))
1417             if (Document *doc = frame->document())
1418                 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1419         return jsUndefined();
1420   case Window::RemoveEventListener:
1421         if (!window->isSafeScript(exec))
1422             return jsUndefined();
1423         if (JSEventListener *listener = Window::retrieveActive(exec)->findJSEventListener(args[1]))
1424             if (Document *doc = frame->document())
1425                 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1426         return jsUndefined();
1427   case Window::ShowModalDialog: {
1428     JSValue* result = showModalDialog(exec, window, args);
1429     return result;
1430   }
1431   }
1432   return jsUndefined();
1433 }
1434
1435 void Window::updateLayout() const
1436 {
1437   Frame* frame = impl()->frame();
1438   if (!frame)
1439     return;
1440   WebCore::Document* docimpl = frame->document();
1441   if (docimpl)
1442     docimpl->updateLayoutIgnorePendingStylesheets();
1443 }
1444
1445 void Window::setReturnValueSlot(JSValue **slot)
1446
1447     d->m_returnValueSlot = slot; 
1448 }
1449
1450 ////////////////////// ScheduledAction ////////////////////////
1451
1452 void ScheduledAction::execute(Window* window)
1453 {
1454     RefPtr<Frame> frame = window->impl()->frame();
1455     if (!frame)
1456         return;
1457
1458     KJSProxy* scriptProxy = frame->scriptProxy();
1459     if (!scriptProxy)
1460         return;
1461
1462     RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
1463
1464     interpreter->setProcessingTimerCallback(true);
1465
1466     if (JSValue* func = m_func.get()) {
1467         JSLock lock;
1468         if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
1469             ExecState* exec = interpreter->globalExec();
1470             ASSERT(window == interpreter->globalObject());
1471             interpreter->startTimeoutCheck();
1472             static_cast<JSObject*>(func)->call(exec, window, m_args);
1473             interpreter->stopTimeoutCheck();
1474             if (exec->hadException()) {
1475                 JSObject* exception = exec->exception()->toObject(exec);
1476                 exec->clearException();
1477                 String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
1478                 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1479                 if (Interpreter::shouldPrintExceptions())
1480                     printf("(timer):%s\n", message.utf8().data());
1481                 if (Page* page = frame->page())
1482                     page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
1483             }
1484         }
1485     } else
1486         frame->loader()->executeScript(m_code);
1487
1488     // Update our document's rendering following the execution of the timeout callback.
1489     // FIXME: Why not use updateDocumentsRendering to update rendering of all documents?
1490     // FIXME: Is this really the right point to do the update? We need a place that works
1491     // for all possible entry points that might possibly execute script, but this seems
1492     // to be a bit too low-level.
1493     if (Document* doc = frame->document())
1494         doc->updateRendering();
1495   
1496     interpreter->setProcessingTimerCallback(false);
1497 }
1498
1499 ////////////////////// timeouts ////////////////////////
1500
1501 void Window::clearAllTimeouts()
1502 {
1503     deleteAllValues(d->m_timeouts);
1504     d->m_timeouts.clear();
1505 }
1506
1507 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1508 {
1509     int timeoutId = ++lastUsedTimeoutId;
1510     int nestLevel = timerNestingLevel + 1;
1511     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1512     ASSERT(!d->m_timeouts.get(timeoutId));
1513     d->m_timeouts.set(timeoutId, timer);
1514     // Use a minimum interval of 10 ms to match other browsers, but only once we've
1515     // nested enough to notice that we're repeating.
1516     // Faster timers might be "better", but they're incompatible.
1517     double interval = max(0.001, t * 0.001);
1518     if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1519         interval = cMinimumTimerInterval;
1520     if (singleShot)
1521         timer->startOneShot(interval);
1522     else
1523         timer->startRepeating(interval);
1524     return timeoutId;
1525 }
1526
1527 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1528 {
1529     return installTimeout(new ScheduledAction(handler), t, singleShot);
1530 }
1531
1532 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1533 {
1534     return installTimeout(new ScheduledAction(func, args), t, singleShot);
1535 }
1536
1537 PausedTimeouts* Window::pauseTimeouts()
1538 {
1539     size_t count = d->m_timeouts.size();
1540     if (count == 0)
1541         return 0;
1542
1543     PausedTimeout* t = new PausedTimeout [count];
1544     PausedTimeouts* result = new PausedTimeouts(t, count);
1545
1546     WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1547     for (size_t i = 0; i != count; ++i, ++it) {
1548         int timeoutId = it->first;
1549         DOMWindowTimer* timer = it->second;
1550         t[i].timeoutId = timeoutId;
1551         t[i].nestingLevel = timer->nestingLevel();
1552         t[i].nextFireInterval = timer->nextFireInterval();
1553         t[i].repeatInterval = timer->repeatInterval();
1554         t[i].action = timer->takeAction();
1555     }
1556     ASSERT(it == d->m_timeouts.end());
1557
1558     deleteAllValues(d->m_timeouts);
1559     d->m_timeouts.clear();
1560
1561     return result;
1562 }
1563
1564 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1565 {
1566     if (!timeouts)
1567         return;
1568     size_t count = timeouts->numTimeouts();
1569     PausedTimeout* array = timeouts->takeTimeouts();
1570     for (size_t i = 0; i != count; ++i) {
1571         int timeoutId = array[i].timeoutId;
1572         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1573         d->m_timeouts.set(timeoutId, timer);
1574         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1575     }
1576     delete [] array;
1577 }
1578
1579 void Window::clearTimeout(int timeoutId, bool delAction)
1580 {
1581     WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.find(timeoutId);
1582     if (it == d->m_timeouts.end())
1583         return;
1584     DOMWindowTimer* timer = it->second;
1585     d->m_timeouts.remove(it);
1586     delete timer;
1587 }
1588
1589 void Window::timerFired(DOMWindowTimer* timer)
1590 {
1591     // Simple case for non-one-shot timers.
1592     if (timer->isActive()) {
1593         int timeoutId = timer->timeoutId();
1594
1595         timer->action()->execute(this);
1596         if (d->m_timeouts.contains(timeoutId) && timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1597             timer->setNestingLevel(timer->nestingLevel() + 1);
1598             if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1599                 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1600         }
1601         return;
1602     }
1603
1604     // Delete timer before executing the action for one-shot timers.
1605     ScheduledAction* action = timer->takeAction();
1606     d->m_timeouts.remove(timer->timeoutId());
1607     delete timer;
1608     action->execute(this);
1609     
1610     JSLock lock;
1611     delete action;
1612 }
1613
1614 void Window::disconnectFrame()
1615 {
1616     clearAllTimeouts();
1617     if (d->loc)
1618         d->loc->m_frame = 0;
1619 }
1620
1621 Window::ListenersMap& Window::jsEventListeners()
1622 {
1623     return d->jsEventListeners;
1624 }
1625
1626 Window::ListenersMap& Window::jsHTMLEventListeners()
1627 {
1628     return d->jsHTMLEventListeners;
1629 }
1630
1631 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1632 {
1633     return d->jsUnprotectedEventListeners;
1634 }
1635
1636 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1637 {
1638     return d->jsUnprotectedHTMLEventListeners;
1639 }
1640
1641 ////////////////////// Location Object ////////////////////////
1642
1643 const ClassInfo Location::info = { "Location", 0, &LocationTable, 0 };
1644 /*
1645 @begin LocationTable 12
1646   assign        Location::Assign        DontDelete|Function 1
1647   hash          Location::Hash          DontDelete
1648   host          Location::Host          DontDelete
1649   hostname      Location::Hostname      DontDelete
1650   href          Location::Href          DontDelete
1651   pathname      Location::Pathname      DontDelete
1652   port          Location::Port          DontDelete
1653   protocol      Location::Protocol      DontDelete
1654   search        Location::Search        DontDelete
1655   toString      Location::ToString      DontEnum|DontDelete|Function 0
1656   replace       Location::Replace       DontDelete|Function 1
1657   reload        Location::Reload        DontDelete|Function 0
1658 @end
1659 */
1660 KJS_IMPLEMENT_PROTOTYPE_FUNCTION(LocationFunc)
1661 Location::Location(Frame *p) : m_frame(p)
1662 {
1663 }
1664
1665 JSValue *Location::getValueProperty(ExecState *exec, int token) const
1666 {
1667   KURL url = m_frame->loader()->url();
1668   switch (token) {
1669   case Hash:
1670     return jsString(url.ref().isNull() ? "" : "#" + url.ref());
1671   case Host: {
1672     // Note: this is the IE spec. The NS spec swaps the two, it says
1673     // "The hostname property is the concatenation of the host and port properties, separated by a colon."
1674     // Bleh.
1675     UString str = url.host();
1676     if (url.port())
1677         str += ":" + String::number((int)url.port());
1678     return jsString(str);
1679   }
1680   case Hostname:
1681     return jsString(url.host());
1682   case Href:
1683     if (!url.hasPath())
1684       return jsString(url.prettyURL() + "/");
1685     return jsString(url.prettyURL());
1686   case Pathname:
1687     return jsString(url.path().isEmpty() ? "/" : url.path());
1688   case Port:
1689     return jsString(url.port() ? String::number((int)url.port()) : "");
1690   case Protocol:
1691     return jsString(url.protocol() + ":");
1692   case Search:
1693     return jsString(url.query());
1694   default:
1695     ASSERT(0);
1696     return jsUndefined();
1697   }
1698 }
1699
1700 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) 
1701 {
1702   if (!m_frame)
1703     return false;
1704   
1705   const Window* window = Window::retrieveWindow(m_frame);
1706   
1707   const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
1708   if (!entry || (entry->value != Replace && entry->value != Reload && entry->value != Assign))
1709     if (!window || !window->isSafeScript(exec)) {
1710       slot.setUndefined(this);
1711       return true;
1712     }
1713
1714   return getStaticPropertySlot<LocationFunc, Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
1715 }
1716
1717 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
1718 {
1719   if (!m_frame)
1720     return;
1721
1722   DeprecatedString str = v->toString(exec);
1723   KURL url = m_frame->loader()->url();
1724   const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
1725   if (entry)
1726     switch (entry->value) {
1727     case Href: {
1728       Frame* p = Window::retrieveActive(exec)->impl()->frame();
1729       if ( p )
1730         url = p->loader()->completeURL(str).url();
1731       else
1732         url = str;
1733       break;
1734     }
1735     case Hash: {
1736       if (str.startsWith("#"))
1737         str = str.mid(1);
1738
1739       if (url.ref() == str)
1740           return;
1741
1742       url.setRef(str);
1743       break;
1744     }
1745     case Host: {
1746       url.setHostAndPort(str);
1747       break;
1748     }
1749     case Hostname:
1750       url.setHost(str);
1751       break;
1752     case Pathname:
1753       url.setPath(str);
1754       break;
1755     case Port:
1756       url.setPort(str.toUInt());
1757       break;
1758     case Protocol:
1759       url.setProtocol(str);
1760       break;
1761     case Search:
1762       url.setQuery(str);
1763       break;
1764     default:
1765       // Disallow changing other properties in LocationTable. e.g., "window.location.toString = ...".
1766       // <http://bugs.webkit.org/show_bug.cgi?id=12720>
1767       return;
1768     }
1769   else {
1770     JSObject::put(exec, p, v, attr);
1771     return;
1772   }
1773
1774   const Window* window = Window::retrieveWindow(m_frame);
1775   Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1776   if (!url.url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1777     bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1778     m_frame->loader()->scheduleLocationChange(url.url(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
1779   }
1780 }
1781
1782 JSValue *LocationFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
1783 {
1784   if (!thisObj->inherits(&Location::info))
1785     return throwError(exec, TypeError);
1786   Location *location = static_cast<Location *>(thisObj);
1787   Frame *frame = location->frame();
1788   if (frame) {
1789       
1790     Window* window = Window::retrieveWindow(frame);
1791     if (id != Location::Replace && !window->isSafeScript(exec))
1792         return jsUndefined();
1793       
1794     switch (id) {
1795     case Location::Replace:
1796     {
1797       DeprecatedString str = args[0]->toString(exec);
1798       Frame* p = Window::retrieveActive(exec)->impl()->frame();
1799       if ( p ) {
1800         const Window* window = Window::retrieveWindow(frame);
1801         if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1802           bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1803           frame->loader()->scheduleLocationChange(p->loader()->completeURL(str).url(), p->loader()->outgoingReferrer(), true, userGesture);
1804         }
1805       }
1806       break;
1807     }
1808     case Location::Reload:
1809     {
1810       const Window* window = Window::retrieveWindow(frame);
1811       if (!frame->loader()->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1812         bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1813         frame->loader()->scheduleRefresh(userGesture);
1814       }
1815       break;
1816     }
1817     case Location::Assign:
1818     {
1819         Frame *p = Window::retrieveActive(exec)->impl()->frame();
1820         if (p) {
1821             const Window *window = Window::retrieveWindow(frame);
1822             DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(args[0]->toString(exec))).url();
1823             if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1824                 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1825                 // We want a new history item if this JS was called via a user gesture
1826                 frame->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
1827             }
1828         }
1829         break;
1830     }
1831     case Location::ToString:
1832         if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
1833             return jsString();
1834
1835         if (!frame->loader()->url().hasPath())
1836             return jsString(frame->loader()->url().prettyURL() + "/");
1837         return jsString(frame->loader()->url().prettyURL());
1838     }
1839   }
1840   return jsUndefined();
1841 }
1842
1843 /////////////////////////////////////////////////////////////////////////////
1844
1845 PausedTimeouts::~PausedTimeouts()
1846 {
1847     PausedTimeout *array = m_array;
1848     if (!array)
1849         return;
1850     size_t count = m_length;
1851     for (size_t i = 0; i != count; ++i)
1852         delete array[i].action;
1853     delete [] array;
1854 }
1855
1856 void DOMWindowTimer::fired()
1857 {
1858     timerNestingLevel = m_nestingLevel;
1859     m_object->timerFired(this);
1860     timerNestingLevel = 0;
1861 }
1862
1863 } // namespace KJS
1864
1865 using namespace KJS;
1866
1867 namespace WebCore {
1868
1869 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1870 {
1871     if (!domWindow)
1872         return jsNull();
1873     Frame* frame = domWindow->frame();
1874     if (!frame)
1875         return jsNull();
1876     return Window::retrieve(frame);
1877 }
1878     
1879 } // namespace WebCore