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