a123c07b69c3fa0050effdfa0cb72358bccbdc2e
[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         if (!p->loader()->shouldAllowNavigation(impl()->frame()))
735           return;
736         DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(value->toString(exec))).url();
737         if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
738           bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
739           // We want a new history item if this JS was called via a user gesture
740           impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
741         }
742       }
743       return;
744     }
745     case Onabort:
746       if (isSafeScript(exec))
747         setListener(exec, abortEvent,value);
748       return;
749     case Onblur:
750       if (isSafeScript(exec))
751         setListener(exec, blurEvent,value);
752       return;
753     case Onchange:
754       if (isSafeScript(exec))
755         setListener(exec, changeEvent,value);
756       return;
757     case Onclick:
758       if (isSafeScript(exec))
759         setListener(exec,clickEvent,value);
760       return;
761     case Ondblclick:
762       if (isSafeScript(exec))
763         setListener(exec, dblclickEvent,value);
764       return;
765     case Onerror:
766       if (isSafeScript(exec))
767         setListener(exec, errorEvent, value);
768       return;
769     case Onfocus:
770       if (isSafeScript(exec))
771         setListener(exec,focusEvent,value);
772       return;
773     case Onkeydown:
774       if (isSafeScript(exec))
775         setListener(exec,keydownEvent,value);
776       return;
777     case Onkeypress:
778       if (isSafeScript(exec))
779         setListener(exec,keypressEvent,value);
780       return;
781     case Onkeyup:
782       if (isSafeScript(exec))
783         setListener(exec,keyupEvent,value);
784       return;
785     case Onload:
786       if (isSafeScript(exec))
787         setListener(exec,loadEvent,value);
788       return;
789     case Onmousedown:
790       if (isSafeScript(exec))
791         setListener(exec,mousedownEvent,value);
792       return;
793     case Onmousemove:
794       if (isSafeScript(exec))
795         setListener(exec,mousemoveEvent,value);
796       return;
797     case Onmouseout:
798       if (isSafeScript(exec))
799         setListener(exec,mouseoutEvent,value);
800       return;
801     case Onmouseover:
802       if (isSafeScript(exec))
803         setListener(exec,mouseoverEvent,value);
804       return;
805     case Onmouseup:
806       if (isSafeScript(exec))
807         setListener(exec,mouseupEvent,value);
808       return;
809     case OnWindowMouseWheel:
810       if (isSafeScript(exec))
811         setListener(exec, mousewheelEvent,value);
812       return;
813     case Onreset:
814       if (isSafeScript(exec))
815         setListener(exec,resetEvent,value);
816       return;
817     case Onresize:
818       if (isSafeScript(exec))
819         setListener(exec,resizeEvent,value);
820       return;
821     case Onscroll:
822       if (isSafeScript(exec))
823         setListener(exec,scrollEvent,value);
824       return;
825     case Onsearch:
826         if (isSafeScript(exec))
827             setListener(exec,searchEvent,value);
828         return;
829     case Onselect:
830       if (isSafeScript(exec))
831         setListener(exec,selectEvent,value);
832       return;
833     case Onsubmit:
834       if (isSafeScript(exec))
835         setListener(exec,submitEvent,value);
836       return;
837     case Onbeforeunload:
838       if (isSafeScript(exec))
839         setListener(exec, beforeunloadEvent, value);
840       return;
841     case Onunload:
842       if (isSafeScript(exec))
843         setListener(exec, unloadEvent, value);
844       return;
845     default:
846       break;
847     }
848   }
849   if (isSafeScript(exec))
850     JSObject::put(exec, propertyName, value, attr);
851 }
852
853 static bool shouldLoadAsEmptyDocument(const KURL &url)
854 {
855   return url.protocol().lower() == "about" || url.isEmpty();
856 }
857
858 bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
859 {
860     if (origin == target)
861         return true;
862         
863     Frame* originFrame = origin->frame();
864     Frame* targetFrame = target->frame();
865
866     // JS may be attempting to access the "window" object, which should be valid,
867     // even if the document hasn't been constructed yet.  If the document doesn't
868     // exist yet allow JS to access the window object.
869     if (!targetFrame->document())
870         return true;
871
872     WebCore::Document *originDocument = originFrame->document();
873     WebCore::Document *targetDocument = targetFrame->document();
874
875     if (!targetDocument) {
876         return false;
877     }
878
879     WebCore::String targetDomain = targetDocument->domain();
880
881     // Always allow local pages to execute any JS.
882     if (targetDomain.isNull())
883         return true;
884
885     WebCore::String originDomain = originDocument->domain();
886
887     // if this document is being initially loaded as empty by its parent
888     // or opener, allow access from any document in the same domain as
889     // the parent or opener.
890     if (shouldLoadAsEmptyDocument(targetFrame->loader()->url())) {
891         Frame* ancestorFrame = targetFrame->loader()->opener() ? targetFrame->loader()->opener() : targetFrame->tree()->parent();
892         while (ancestorFrame && shouldLoadAsEmptyDocument(ancestorFrame->loader()->url()))
893             ancestorFrame = ancestorFrame->tree()->parent();
894         if (ancestorFrame)
895             originDomain = ancestorFrame->document()->domain();
896     }
897
898     if (targetDomain == originDomain)
899         return true;
900
901     if (!originFrame->settings()->privateBrowsingEnabled()) {
902         if (Interpreter::shouldPrintExceptions())
903             printf("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         String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n", 
906                                         targetDocument->URL().latin1(), originDocument->URL().latin1());
907         if (Page* page = targetFrame->page())
908             page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
909     }
910
911     return false;
912 }
913
914 bool Window::isSafeScript(ExecState *exec) const
915 {
916   Frame* frame = impl()->frame();
917   if (!frame)
918     return false;
919   Frame* activeFrame = static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->frame();
920   if (!activeFrame)
921     return false;
922   if (activeFrame == frame)
923     return true;
924
925   WebCore::Document* thisDocument = frame->document();
926
927   // JS may be attempting to access the "window" object, which should be valid,
928   // even if the document hasn't been constructed yet.  If the document doesn't
929   // exist yet allow JS to access the window object.
930   if (!thisDocument)
931       return true;
932
933   WebCore::Document* actDocument = activeFrame->document();
934
935   const SecurityOrigin& actSecurityOrigin = actDocument->securityOrigin();
936   const SecurityOrigin& thisSecurityOrigin = thisDocument->securityOrigin();
937
938   if (actSecurityOrigin.canAccess(thisSecurityOrigin))
939     return true;
940
941     if (!frame->settings()->privateBrowsingEnabled()) {
942         // FIXME: this error message should contain more specifics of why the same origin check has failed.
943         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",
944                                         thisDocument->URL().utf8().data(), actDocument->URL().utf8().data());
945         
946         if (Interpreter::shouldPrintExceptions())
947             printf("%s", message.utf8().data());
948         
949         if (Page* page = frame->page())
950             page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
951     }
952
953   return false;
954 }
955
956 void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
957 {
958   if (!isSafeScript(exec))
959     return;
960   Frame* frame = impl()->frame();
961   if (!frame)
962     return;
963   Document* doc = frame->document();
964   if (!doc)
965     return;
966
967   doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func,true));
968 }
969
970 JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
971 {
972   if (!isSafeScript(exec))
973     return jsUndefined();
974   Frame* frame = impl()->frame();
975   if (!frame)
976     return jsUndefined();
977   Document* doc = frame->document();
978   if (!doc)
979     return jsUndefined();
980
981   WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
982   if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
983     return static_cast<JSEventListener*>(listener)->listenerObj();
984   else
985     return jsNull();
986 }
987
988 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
989 {
990     if (!val->isObject())
991         return 0;
992     JSObject* object = static_cast<JSObject*>(val);
993     ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
994     return listeners.get(object);
995 }
996
997 JSEventListener *Window::findOrCreateJSEventListener(JSValue *val, bool html)
998 {
999   JSEventListener *listener = findJSEventListener(val, html);
1000   if (listener)
1001     return listener;
1002
1003   if (!val->isObject())
1004     return 0;
1005   JSObject *object = static_cast<JSObject *>(val);
1006
1007   // Note that the JSEventListener constructor adds it to our jsEventListeners list
1008   return new JSEventListener(object, this, html);
1009 }
1010
1011 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1012 {
1013     if (!val->isObject())
1014         return 0;
1015     JSObject* object = static_cast<JSObject*>(val);
1016     UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1017     return listeners.get(object);
1018 }
1019
1020 JSUnprotectedEventListener *Window::findOrCreateJSUnprotectedEventListener(JSValue *val, bool html)
1021 {
1022   JSUnprotectedEventListener *listener = findJSUnprotectedEventListener(val, html);
1023   if (listener)
1024     return listener;
1025
1026   if (!val->isObject())
1027     return 0;
1028   JSObject *object = static_cast<JSObject *>(val);
1029
1030   // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1031   return new JSUnprotectedEventListener(object, this, html);
1032 }
1033
1034 void Window::clearHelperObjectProperties()
1035 {
1036   d->loc = 0;
1037   d->m_evt = 0;
1038 }
1039
1040 void Window::clear()
1041 {
1042   JSLock lock;
1043
1044   if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1045     *d->m_returnValueSlot = getDirect("returnValue");
1046
1047   clearAllTimeouts();
1048   clearProperties();
1049   clearHelperObjectProperties();
1050   setPrototype(JSDOMWindowPrototype::self()); // clear the prototype
1051
1052   // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1053   // disconnected yet
1054   if (Frame* frame = impl()->frame())
1055     frame->scriptProxy()->interpreter()->resetGlobalObjectProperties();
1056
1057   // there's likely to be lots of garbage now
1058   gcController().garbageCollectSoon();
1059 }
1060
1061 void Window::setCurrentEvent(Event *evt)
1062 {
1063   d->m_evt = evt;
1064 }
1065
1066 static void setWindowFeature(const String& keyString, const String& valueString, WindowFeatures& windowFeatures)
1067 {
1068     int value;
1069     
1070     if (valueString.length() == 0 || // listing a key with no value is shorthand for key=yes
1071         valueString == "yes")
1072         value = 1;
1073     else
1074         value = valueString.toInt();
1075     
1076     if (keyString == "left" || keyString == "screenx") {
1077         windowFeatures.xSet = true;
1078         windowFeatures.x = value;
1079     } else if (keyString == "top" || keyString == "screeny") {
1080         windowFeatures.ySet = true;
1081         windowFeatures.y = value;
1082     } else if (keyString == "width" || keyString == "innerwidth") {
1083         windowFeatures.widthSet = true;
1084         windowFeatures.width = value;
1085     } else if (keyString == "height" || keyString == "innerheight") {
1086         windowFeatures.heightSet = true;
1087         windowFeatures.height = value;
1088     } else if (keyString == "menubar")
1089         windowFeatures.menuBarVisible = value;
1090     else if (keyString == "toolbar")
1091         windowFeatures.toolBarVisible = value;
1092     else if (keyString == "location")
1093         windowFeatures.locationBarVisible = value;
1094     else if (keyString == "status")
1095         windowFeatures.statusBarVisible = value;
1096     else if (keyString == "resizable")
1097         windowFeatures.resizable = value;
1098     else if (keyString == "fullscreen")
1099         windowFeatures.fullscreen = value;
1100     else if (keyString == "scrollbars")
1101         windowFeatures.scrollbarsVisible = value;
1102 }
1103
1104 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
1105 static bool isSeparator(::UChar c)
1106 {
1107     return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
1108 }
1109
1110 static void parseWindowFeatures(const String& features, WindowFeatures& windowFeatures)
1111 {
1112     /*
1113      The IE rule is: all features except for channelmode and fullscreen default to YES, but
1114      if the user specifies a feature string, all features default to NO. (There is no public
1115      standard that applies to this method.)
1116      
1117      <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
1118      */
1119     
1120     windowFeatures.dialog = false;
1121     windowFeatures.fullscreen = false;
1122     
1123     windowFeatures.xSet = false;
1124     windowFeatures.ySet = false;
1125     windowFeatures.widthSet = false;
1126     windowFeatures.heightSet = false;
1127     
1128     if (features.length() == 0) {
1129         windowFeatures.menuBarVisible = true;
1130         windowFeatures.statusBarVisible = true;
1131         windowFeatures.toolBarVisible = true;
1132         windowFeatures.locationBarVisible = true;
1133         windowFeatures.scrollbarsVisible = true;
1134         windowFeatures.resizable = true;
1135         
1136         return;
1137     }
1138     
1139     windowFeatures.menuBarVisible = false;
1140     windowFeatures.statusBarVisible = false;
1141     windowFeatures.toolBarVisible = false;
1142     windowFeatures.locationBarVisible = false;
1143     windowFeatures.scrollbarsVisible = false;
1144     windowFeatures.resizable = false;
1145     
1146     // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
1147     int keyBegin, keyEnd;
1148     int valueBegin, valueEnd;
1149     
1150     int i = 0;
1151     int length = features.length();
1152     String buffer = features.lower();
1153     while (i < length) {
1154         // skip to first non-separator, but don't skip past the end of the string
1155         while (isSeparator(buffer[i])) {
1156             if (i >= length)
1157                 break;
1158             i++;
1159         }
1160         keyBegin = i;
1161         
1162         // skip to first separator
1163         while (!isSeparator(buffer[i]))
1164             i++;
1165         keyEnd = i;
1166         
1167         // skip to first '=', but don't skip past a ',' or the end of the string
1168         while (buffer[i] != '=') {
1169             if (buffer[i] == ',' || i >= length)
1170                 break;
1171             i++;
1172         }
1173         
1174         // skip to first non-separator, but don't skip past a ',' or the end of the string
1175         while (isSeparator(buffer[i])) {
1176             if (buffer[i] == ',' || i >= length)
1177                 break;
1178             i++;
1179         }
1180         valueBegin = i;
1181         
1182         // skip to first separator
1183         while (!isSeparator(buffer[i]))
1184             i++;
1185         valueEnd = i;
1186         
1187         ASSERT(i <= length);
1188
1189         String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
1190         String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
1191         setWindowFeature(keyString, valueString, windowFeatures);
1192     }
1193 }
1194
1195 // Explain the 4 things this function does.
1196 // 1) Validates the pending changes are not changing to NaN
1197 // 2) Constrains the window rect to no smaller than 100 in each dimension and no
1198 //    bigger than the the float rect's dimensions.
1199 // 3) Constrain window rect to within the top and left boundaries of the screen rect
1200 // 4) Constraint the window rect to within the bottom and right boundaries of the
1201 //    screen rect.
1202 // 5) Translate the window rect coordinates to be within the coordinate space of
1203 //    the screen rect.
1204 static void adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges)
1205 {
1206     // Make sure we're in a valid state before adjusting dimensions
1207     ASSERT(!isnan(screen.x()) && !isnan(screen.y()) && !isnan(screen.width()) && !isnan(screen.height()) &&
1208            !isnan(window.x()) && !isnan(window.y()) && !isnan(window.width()) && !isnan(window.height()));
1209     
1210     // Update window values if they are not NaN
1211     if (!isnan(pendingChanges.x()))
1212         window.setX(pendingChanges.x());
1213     if (!isnan(pendingChanges.y()))
1214         window.setY(pendingChanges.y());
1215     if (!isnan(pendingChanges.width()))
1216         window.setWidth(pendingChanges.width());
1217     if (!isnan(pendingChanges.height()))
1218         window.setHeight(pendingChanges.height());
1219     
1220     // Resize the window to between 100 and the screen width and height if it's
1221     // outside of those ranges.
1222     window.setWidth(min(max(100.0f, window.width()), screen.width()));
1223     window.setHeight(min(max(100.0f, window.height()), screen.height()));
1224     
1225     // Constrain the window to the top and left of the screen if it's left or
1226     // above it.
1227     window.setX(max(window.x(), screen.x()));
1228     window.setY(max(window.y(), screen.y()));
1229
1230     // Constrain the window to the bottom and right of the screen if it's past
1231     // the right or below it.
1232     window.setX(window.x() - max(0.0f, window.right() - screen.width() - screen.x()));
1233     window.setY(window.y() - max(0.0f, window.bottom() - screen.height() - screen.y()));
1234 }
1235
1236 JSValue* WindowProtoFuncAToB::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1237 {
1238     if (!thisObj->inherits(&Window::info))
1239         return throwError(exec, TypeError);
1240     Window* window = static_cast<Window*>(thisObj);
1241     Frame* frame = window->impl()->frame();
1242     if (!frame)
1243         return jsUndefined();
1244
1245     JSValue* v = args[0];
1246     UString s = v->toString(exec);
1247
1248     if (args.size() < 1)
1249         return throwError(exec, SyntaxError, "Not enough arguments");
1250     if (v->isNull())
1251         return jsString();
1252     if (!s.is8Bit()) {
1253         setDOMException(exec, INVALID_CHARACTER_ERR);
1254         return jsUndefined();
1255     }
1256     
1257     Vector<char> in(s.size());
1258     for (int i = 0; i < s.size(); ++i)
1259         in[i] = static_cast<char>(s.data()[i].unicode());
1260     Vector<char> out;
1261
1262     if (!base64Decode(in, out))
1263         return throwError(exec, GeneralError, "Cannot decode base64");
1264     
1265     return jsString(String(out.data(), out.size()));
1266 }
1267
1268 JSValue* WindowProtoFuncBToA::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1269 {
1270     if (!thisObj->inherits(&Window::info))
1271         return throwError(exec, TypeError);
1272     Window* window = static_cast<Window*>(thisObj);
1273     Frame* frame = window->impl()->frame();
1274     if (!frame)
1275         return jsUndefined();
1276
1277     JSValue* v = args[0];
1278     UString s = v->toString(exec);
1279
1280     if (args.size() < 1)
1281         return throwError(exec, SyntaxError, "Not enough arguments");
1282     if (v->isNull())
1283         return jsString();
1284     if (!s.is8Bit()) {
1285         setDOMException(exec, INVALID_CHARACTER_ERR);
1286         return jsUndefined();
1287     }
1288     
1289     Vector<char> in(s.size());
1290     for (int i = 0; i < s.size(); ++i)
1291         in[i] = static_cast<char>(s.data()[i].unicode());
1292     Vector<char> out;
1293
1294     base64Encode(in, out);
1295     
1296     return jsString(String(out.data(), out.size()));
1297 }
1298
1299 JSValue* WindowProtoFuncOpen::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1300 {
1301     if (!thisObj->inherits(&Window::info))
1302         return throwError(exec, TypeError);
1303     Window* window = static_cast<Window*>(thisObj);
1304     Frame* frame = window->impl()->frame();
1305     if (!frame)
1306         return jsUndefined();
1307
1308     Page* page = frame->page();
1309
1310     String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1311     AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1312
1313     // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1314     // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1315     if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1316         return jsUndefined();
1317
1318     // Get the target frame for the special cases of _top and _parent
1319     if (frameName == "_top")
1320         while (frame->tree()->parent())
1321               frame = frame->tree()->parent();
1322     else if (frameName == "_parent")
1323         if (frame->tree()->parent())
1324             frame = frame->tree()->parent();
1325
1326     // In those cases, we can schedule a location change right now and return early
1327     if (frameName == "_top" || frameName == "_parent") {
1328         String completedURL;
1329         Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1330         if (!urlString.isEmpty() && activeFrame)
1331             completedURL = activeFrame->document()->completeURL(urlString);
1332
1333         const Window* window = Window::retrieveWindow(frame);
1334         if (!completedURL.isEmpty() && (!completedURL.startsWith("javascript:", false) || (window && window->isSafeScript(exec)))) {
1335             bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1336             frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1337         }
1338         return Window::retrieve(frame);
1339     }
1340     
1341     // In the case of a named frame or a new window, we'll use the createWindow() helper
1342     WindowFeatures windowFeatures;
1343     String features = valueToStringWithUndefinedOrNullCheck(exec, args[2]);
1344     parseWindowFeatures(features, windowFeatures);
1345     FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1346     adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1347
1348     windowFeatures.x = windowRect.x();
1349     windowFeatures.y = windowRect.y();
1350     windowFeatures.height = windowRect.height();
1351     windowFeatures.width = windowRect.width();
1352
1353     frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1354
1355     if (!frame)
1356         return jsUndefined();
1357
1358     return Window::retrieve(frame); // global object
1359 }
1360
1361 JSValue* WindowProtoFuncScrollBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1362 {
1363     if (!thisObj->inherits(&Window::info))
1364         return throwError(exec, TypeError);
1365     Window* window = static_cast<Window*>(thisObj);
1366     Frame* frame = window->impl()->frame();
1367     if (!frame)
1368         return jsUndefined();
1369
1370     FrameView *widget = frame->view();
1371
1372     window->updateLayout();
1373     if(args.size() >= 2 && widget)
1374       widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1375     return jsUndefined();
1376 }
1377
1378 JSValue* WindowProtoFuncScrollTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1379 {
1380     // Also the implementation for window.scroll()
1381
1382     if (!thisObj->inherits(&Window::info))
1383         return throwError(exec, TypeError);
1384     Window* window = static_cast<Window*>(thisObj);
1385     Frame* frame = window->impl()->frame();
1386     if (!frame)
1387         return jsUndefined();
1388
1389     FrameView *widget = frame->view();
1390
1391     window->updateLayout();
1392     if (args.size() >= 2 && widget)
1393       widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1394     return jsUndefined();
1395 }
1396
1397 JSValue* WindowProtoFuncMoveBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1398 {
1399     if (!thisObj->inherits(&Window::info))
1400         return throwError(exec, TypeError);
1401     Window* window = static_cast<Window*>(thisObj);
1402     Frame* frame = window->impl()->frame();
1403     if (!frame)
1404         return jsUndefined();
1405
1406     Page* page = frame->page();
1407
1408     if (args.size() >= 2 && page) {
1409       FloatRect fr = page->chrome()->windowRect();
1410       FloatRect update = fr;
1411       update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1412       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1413       adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1414       page->chrome()->setWindowRect(fr);
1415     }
1416     return jsUndefined();
1417 }
1418
1419 JSValue* WindowProtoFuncMoveTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1420 {
1421     if (!thisObj->inherits(&Window::info))
1422         return throwError(exec, TypeError);
1423     Window* window = static_cast<Window*>(thisObj);
1424     Frame* frame = window->impl()->frame();
1425     if (!frame)
1426         return jsUndefined();
1427
1428     Page* page = frame->page();
1429
1430     if (args.size() >= 2 && page) {
1431       FloatRect fr = page->chrome()->windowRect();
1432       FloatRect sr = screenAvailableRect(page->mainFrame()->view());
1433       fr.setLocation(sr.location());
1434       FloatRect update = fr;
1435       update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));     
1436       // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1437       adjustWindowRect(sr, fr, update);
1438       page->chrome()->setWindowRect(fr);
1439     }
1440     return jsUndefined();
1441 }
1442
1443 JSValue* WindowProtoFuncResizeBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1444 {
1445     if (!thisObj->inherits(&Window::info))
1446         return throwError(exec, TypeError);
1447     Window* window = static_cast<Window*>(thisObj);
1448     Frame* frame = window->impl()->frame();
1449     if (!frame)
1450         return jsUndefined();
1451
1452     Page* page = frame->page();
1453
1454     if (args.size() >= 2 && page) {
1455       FloatRect fr = page->chrome()->windowRect();
1456       FloatSize dest = fr.size() + FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1457       FloatRect update(fr.location(), dest);
1458       adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1459       page->chrome()->setWindowRect(fr);
1460     }
1461     return jsUndefined();
1462 }
1463
1464 JSValue* WindowProtoFuncResizeTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1465 {
1466     if (!thisObj->inherits(&Window::info))
1467         return throwError(exec, TypeError);
1468     Window* window = static_cast<Window*>(thisObj);
1469     Frame* frame = window->impl()->frame();
1470     if (!frame)
1471         return jsUndefined();
1472
1473     Page* page = frame->page();
1474
1475     if (args.size() >= 2 && page) {
1476       FloatRect fr = page->chrome()->windowRect();
1477       FloatSize dest = FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1478       FloatRect update(fr.location(), dest);
1479       adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1480       page->chrome()->setWindowRect(fr);
1481     }
1482     return jsUndefined();
1483 }
1484
1485 JSValue* WindowProtoFuncSetTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1486 {
1487     if (!thisObj->inherits(&Window::info))
1488         return throwError(exec, TypeError);
1489     Window* window = static_cast<Window*>(thisObj);
1490     Frame* frame = window->impl()->frame();
1491     if (!frame)
1492         return jsUndefined();
1493
1494     JSValue *v = args[0];
1495     UString s = v->toString(exec);
1496
1497     if (!window->isSafeScript(exec))
1498         return jsUndefined();
1499     if (v->isString()) {
1500       int i = args[1]->toInt32(exec);
1501       int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1502       return jsNumber(r);
1503     }
1504     else if (v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1505       JSValue *func = args[0];
1506       int i = args[1]->toInt32(exec);
1507       
1508       List argsTail;
1509       args.getSlice(2, argsTail);
1510
1511       int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, true /*single shot*/);
1512       return jsNumber(r);
1513     }
1514     else
1515       return jsUndefined();
1516 }
1517
1518 JSValue* WindowProtoFuncClearTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1519 {
1520     // Also the implementation for window.clearInterval()
1521
1522     if (!thisObj->inherits(&Window::info))
1523         return throwError(exec, TypeError);
1524     Window* window = static_cast<Window*>(thisObj);
1525     Frame* frame = window->impl()->frame();
1526     if (!frame)
1527         return jsUndefined();
1528
1529     JSValue *v = args[0];
1530
1531     if (!window->isSafeScript(exec))
1532         return jsUndefined();
1533     (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1534     return jsUndefined();
1535 }
1536
1537 JSValue* WindowProtoFuncSetInterval::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1538 {
1539     if (!thisObj->inherits(&Window::info))
1540         return throwError(exec, TypeError);
1541     Window* window = static_cast<Window*>(thisObj);
1542     Frame* frame = window->impl()->frame();
1543     if (!frame)
1544         return jsUndefined();
1545
1546     JSValue *v = args[0];
1547     UString s = v->toString(exec);
1548
1549     if (!window->isSafeScript(exec))
1550         return jsUndefined();
1551     if (args.size() >= 2 && v->isString()) {
1552       int i = args[1]->toInt32(exec);
1553       int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1554       return jsNumber(r);
1555     }
1556     else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1557       JSValue *func = args[0];
1558       int i = args[1]->toInt32(exec);
1559
1560       List argsTail;
1561       args.getSlice(2, argsTail);
1562
1563       int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, false);
1564       return jsNumber(r);
1565     }
1566     else
1567       return jsUndefined();
1568
1569 }
1570
1571 JSValue* WindowProtoFuncAddEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1572 {
1573     if (!thisObj->inherits(&Window::info))
1574         return throwError(exec, TypeError);
1575     Window* window = static_cast<Window*>(thisObj);
1576     Frame* frame = window->impl()->frame();
1577     if (!frame)
1578         return jsUndefined();
1579
1580     if (!window->isSafeScript(exec))
1581         return jsUndefined();
1582     if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1]))
1583         if (Document *doc = frame->document())
1584             doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1585     return jsUndefined();
1586 }
1587
1588 JSValue* WindowProtoFuncRemoveEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1589 {
1590     if (!thisObj->inherits(&Window::info))
1591         return throwError(exec, TypeError);
1592     Window* window = static_cast<Window*>(thisObj);
1593     Frame* frame = window->impl()->frame();
1594     if (!frame)
1595         return jsUndefined();
1596
1597     if (!window->isSafeScript(exec))
1598         return jsUndefined();
1599     if (JSEventListener* listener = window->findJSEventListener(args[1]))
1600         if (Document *doc = frame->document())
1601             doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1602     return jsUndefined();
1603 }
1604
1605 JSValue* WindowProtoFuncShowModalDialog::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1606 {
1607     if (!thisObj->inherits(&Window::info))
1608         return throwError(exec, TypeError);
1609     Window* window = static_cast<Window*>(thisObj);
1610     Frame* frame = window->impl()->frame();
1611     if (!frame)
1612         return jsUndefined();
1613
1614     return showModalDialog(exec, window, args);
1615 }
1616
1617 JSValue* WindowProtoFuncNotImplemented::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1618 {
1619     if (!thisObj->inherits(&Window::info))
1620         return throwError(exec, TypeError);
1621     Window* window = static_cast<Window*>(thisObj);
1622     Frame* frame = window->impl()->frame();
1623     if (!frame)
1624         return jsUndefined();
1625
1626     // If anyone implements these, they need the safe script security check.
1627     if (!window->isSafeScript(exec))
1628         return jsUndefined();
1629     // Not implemented.
1630     return jsUndefined();
1631 }
1632
1633 void Window::updateLayout() const
1634 {
1635   Frame* frame = impl()->frame();
1636   if (!frame)
1637     return;
1638   WebCore::Document* docimpl = frame->document();
1639   if (docimpl)
1640     docimpl->updateLayoutIgnorePendingStylesheets();
1641 }
1642
1643 void Window::setReturnValueSlot(JSValue** slot)
1644
1645     d->m_returnValueSlot = slot; 
1646 }
1647
1648 ////////////////////// ScheduledAction ////////////////////////
1649
1650 void ScheduledAction::execute(Window* window)
1651 {
1652     RefPtr<Frame> frame = window->impl()->frame();
1653     if (!frame)
1654         return;
1655
1656     KJSProxy* scriptProxy = frame->scriptProxy();
1657     if (!scriptProxy)
1658         return;
1659
1660     RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
1661
1662     interpreter->setProcessingTimerCallback(true);
1663
1664     if (JSValue* func = m_func.get()) {
1665         JSLock lock;
1666         if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
1667             ExecState* exec = interpreter->globalExec();
1668             ASSERT(window == interpreter->globalObject());
1669             
1670             List args;
1671             size_t size = m_args.size();
1672             for (size_t i = 0; i < size; ++i) {
1673                 args.append(m_args[i]);
1674             }
1675
1676             interpreter->startTimeoutCheck();
1677             static_cast<JSObject*>(func)->call(exec, window, args);
1678             interpreter->stopTimeoutCheck();
1679             if (exec->hadException()) {
1680                 JSObject* exception = exec->exception()->toObject(exec);
1681                 exec->clearException();
1682                 String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
1683                 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1684                 if (Interpreter::shouldPrintExceptions())
1685                     printf("(timer):%s\n", message.utf8().data());
1686                 if (Page* page = frame->page())
1687                     page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
1688             }
1689         }
1690     } else
1691         frame->loader()->executeScript(m_code);
1692
1693     // Update our document's rendering following the execution of the timeout callback.
1694     // FIXME: Why not use updateDocumentsRendering to update rendering of all documents?
1695     // FIXME: Is this really the right point to do the update? We need a place that works
1696     // for all possible entry points that might possibly execute script, but this seems
1697     // to be a bit too low-level.
1698     if (Document* doc = frame->document())
1699         doc->updateRendering();
1700   
1701     interpreter->setProcessingTimerCallback(false);
1702 }
1703
1704 ////////////////////// timeouts ////////////////////////
1705
1706 void Window::clearAllTimeouts()
1707 {
1708     deleteAllValues(d->m_timeouts);
1709     d->m_timeouts.clear();
1710 }
1711
1712 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1713 {
1714     int timeoutId = ++lastUsedTimeoutId;
1715
1716     // avoid wraparound going negative on us
1717     if (timeoutId <= 0)
1718         timeoutId = 1;
1719
1720     int nestLevel = timerNestingLevel + 1;
1721     DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1722     ASSERT(!d->m_timeouts.get(timeoutId));
1723     d->m_timeouts.set(timeoutId, timer);
1724     // Use a minimum interval of 10 ms to match other browsers, but only once we've
1725     // nested enough to notice that we're repeating.
1726     // Faster timers might be "better", but they're incompatible.
1727     double interval = max(0.001, t * 0.001);
1728     if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1729         interval = cMinimumTimerInterval;
1730     if (singleShot)
1731         timer->startOneShot(interval);
1732     else
1733         timer->startRepeating(interval);
1734     return timeoutId;
1735 }
1736
1737 ScheduledAction::ScheduledAction(JSValue* func, const List& args)
1738     : m_func(func)
1739 {
1740     List::const_iterator end = args.end();
1741     for (List::const_iterator it = args.begin(); it != end; ++it)
1742         m_args.append(*it);
1743 }
1744
1745 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1746 {
1747     return installTimeout(new ScheduledAction(handler), t, singleShot);
1748 }
1749
1750 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1751 {
1752     return installTimeout(new ScheduledAction(func, args), t, singleShot);
1753 }
1754
1755 PausedTimeouts* Window::pauseTimeouts()
1756 {
1757     size_t count = d->m_timeouts.size();
1758     if (count == 0)
1759         return 0;
1760
1761     PausedTimeout* t = new PausedTimeout [count];
1762     PausedTimeouts* result = new PausedTimeouts(t, count);
1763
1764     WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1765     for (size_t i = 0; i != count; ++i, ++it) {
1766         int timeoutId = it->first;
1767         DOMWindowTimer* timer = it->second;
1768         t[i].timeoutId = timeoutId;
1769         t[i].nestingLevel = timer->nestingLevel();
1770         t[i].nextFireInterval = timer->nextFireInterval();
1771         t[i].repeatInterval = timer->repeatInterval();
1772         t[i].action = timer->takeAction();
1773     }
1774     ASSERT(it == d->m_timeouts.end());
1775
1776     deleteAllValues(d->m_timeouts);
1777     d->m_timeouts.clear();
1778
1779     return result;
1780 }
1781
1782 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1783 {
1784     if (!timeouts)
1785         return;
1786     size_t count = timeouts->numTimeouts();
1787     PausedTimeout* array = timeouts->takeTimeouts();
1788     for (size_t i = 0; i != count; ++i) {
1789         int timeoutId = array[i].timeoutId;
1790         DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1791         d->m_timeouts.set(timeoutId, timer);
1792         timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1793     }
1794     delete [] array;
1795 }
1796
1797 void Window::clearTimeout(int timeoutId, bool delAction)
1798 {
1799     // timeout IDs have to be positive, and 0 and -1 are unsafe to
1800     // even look up since they are the empty and deleted value
1801     // respectively
1802     if (timeoutId <= 0)
1803         return;
1804
1805     delete d->m_timeouts.take(timeoutId);
1806 }
1807
1808 void Window::timerFired(DOMWindowTimer* timer)
1809 {
1810     // Simple case for non-one-shot timers.
1811     if (timer->isActive()) {
1812         int timeoutId = timer->timeoutId();
1813
1814         timer->action()->execute(this);
1815         if (d->m_timeouts.contains(timeoutId) && timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1816             timer->setNestingLevel(timer->nestingLevel() + 1);
1817             if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1818                 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1819         }
1820         return;
1821     }
1822
1823     // Delete timer before executing the action for one-shot timers.
1824     ScheduledAction* action = timer->takeAction();
1825     d->m_timeouts.remove(timer->timeoutId());
1826     delete timer;
1827     action->execute(this);
1828     
1829     JSLock lock;
1830     delete action;
1831 }
1832
1833 void Window::disconnectFrame()
1834 {
1835     clearAllTimeouts();
1836     if (d->loc)
1837         d->loc->m_frame = 0;
1838 }
1839
1840 Window::ListenersMap& Window::jsEventListeners()
1841 {
1842     return d->jsEventListeners;
1843 }
1844
1845 Window::ListenersMap& Window::jsHTMLEventListeners()
1846 {
1847     return d->jsHTMLEventListeners;
1848 }
1849
1850 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1851 {
1852     return d->jsUnprotectedEventListeners;
1853 }
1854
1855 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1856 {
1857     return d->jsUnprotectedHTMLEventListeners;
1858 }
1859
1860 ////////////////////// Location Object ////////////////////////
1861
1862 const ClassInfo Location::info = { "Location", 0, &LocationTable };
1863 /*
1864 @begin LocationTable 12
1865   assign        &LocationProtoFuncAssign::create        DontDelete|Function 1
1866   hash          Location::Hash                          DontDelete
1867   host          Location::Host                          DontDelete
1868   hostname      Location::Hostname                      DontDelete
1869   href          Location::Href                          DontDelete
1870   pathname      Location::Pathname                      DontDelete
1871   port          Location::Port                          DontDelete
1872   protocol      Location::Protocol                      DontDelete
1873   search        Location::Search                        DontDelete
1874   toString      &LocationProtoFuncToString::create      DontEnum|DontDelete|Function 0
1875   replace       &LocationProtoFuncReplace::create       DontDelete|Function 1
1876   reload        &LocationProtoFuncReload::create        DontDelete|Function 0
1877 @end
1878 */
1879
1880 Location::Location(Frame *p) : m_frame(p)
1881 {
1882 }
1883
1884 JSValue *Location::getValueProperty(ExecState *exec, int token) const
1885 {
1886   KURL url = m_frame->loader()->url();
1887   switch (token) {
1888   case Hash:
1889     return jsString(url.ref().isNull() ? "" : "#" + url.ref());
1890   case Host: {
1891     // Note: this is the IE spec. The NS spec swaps the two, it says
1892     // "The hostname property is the concatenation of the host and port properties, separated by a colon."
1893     // Bleh.
1894     UString str = url.host();
1895     if (url.port())
1896         str += ":" + String::number((int)url.port());
1897     return jsString(str);
1898   }
1899   case Hostname:
1900     return jsString(url.host());
1901   case Href:
1902     if (!url.hasPath())
1903       return jsString(url.prettyURL() + "/");
1904     return jsString(url.prettyURL());
1905   case Pathname:
1906     return jsString(url.path().isEmpty() ? "/" : url.path());
1907   case Port:
1908     return jsString(url.port() ? String::number((int)url.port()) : "");
1909   case Protocol:
1910     return jsString(url.protocol() + ":");
1911   case Search:
1912     return jsString(url.query());
1913   default:
1914     ASSERT(0);
1915     return jsUndefined();
1916   }
1917 }
1918
1919 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) 
1920 {
1921   if (!m_frame)
1922     return false;
1923   
1924   const Window* window = Window::retrieveWindow(m_frame);
1925   
1926   const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
1927   if (!entry || !(entry->attr & KJS::Function) || (entry->value.functionValue != &LocationProtoFuncReplace::create
1928                                                    && entry->value.functionValue != &LocationProtoFuncReload::create
1929                                                    && entry->value.functionValue != &LocationProtoFuncAssign::create))  {
1930     if (!window || !window->isSafeScript(exec)) {
1931       slot.setUndefined(this);
1932       return true;
1933     }
1934   }
1935
1936   return getStaticPropertySlot<Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
1937 }
1938
1939 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
1940 {
1941   if (!m_frame)
1942     return;
1943
1944   DeprecatedString str = v->toString(exec);
1945   KURL url = m_frame->loader()->url();
1946   const Window* window = Window::retrieveWindow(m_frame);
1947   bool sameDomainAccess = window && window->isSafeScript(exec);
1948
1949   const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
1950
1951   if (entry) {
1952       // cross-domain access to the location is allowed when assigning the whole location,
1953       // but not when assigning the individual pieces, since that might inadvertently
1954       // disclose other parts of the original location.
1955       if (entry->value.intValue != Href && !sameDomainAccess)
1956           return;
1957
1958       switch (entry->value.intValue) {
1959       case Href: {
1960           Frame* frame = Window::retrieveActive(exec)->impl()->frame();
1961           if (!frame)
1962               return;
1963           if (!frame->loader()->shouldAllowNavigation(m_frame))
1964               return;
1965           url = frame->loader()->completeURL(str).url();
1966           break;
1967       } 
1968       case Hash: {
1969           if (str.startsWith("#"))
1970               str = str.mid(1);
1971           
1972           if (url.ref() == str)
1973               return;
1974
1975           url.setRef(str);
1976           break;
1977       }
1978       case Host: {
1979           url.setHostAndPort(str);
1980           break;
1981       }
1982       case Hostname:
1983           url.setHost(str);
1984           break;
1985       case Pathname:
1986           url.setPath(str);
1987           break;
1988       case Port:
1989           url.setPort(str.toUInt());
1990           break;
1991       case Protocol:
1992           url.setProtocol(str);
1993           break;
1994       case Search:
1995           url.setQuery(str);
1996           break;
1997       default:
1998           // Disallow changing other properties in LocationTable. e.g., "window.location.toString = ...".
1999           // <http://bugs.webkit.org/show_bug.cgi?id=12720>
2000           return;
2001       }
2002   } else {
2003       if (sameDomainAccess)
2004           JSObject::put(exec, p, v, attr);
2005       return;
2006   }
2007
2008   Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
2009   if (!url.url().startsWith("javascript:", false) || sameDomainAccess) {
2010     bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2011     m_frame->loader()->scheduleLocationChange(url.url(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
2012   }
2013 }
2014
2015 JSValue* LocationProtoFuncReplace::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2016 {
2017     if (!thisObj->inherits(&Location::info))
2018         return throwError(exec, TypeError);
2019     Location* location = static_cast<Location*>(thisObj);
2020     Frame* frame = location->frame();
2021     if (!frame)
2022         return jsUndefined();
2023
2024     Frame* p = Window::retrieveActive(exec)->impl()->frame();
2025     if (p) {
2026       if (!p->loader()->shouldAllowNavigation(frame))
2027         return jsUndefined();
2028       DeprecatedString str = args[0]->toString(exec);
2029       const Window* window = Window::retrieveWindow(frame);
2030       if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2031         bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2032         frame->loader()->scheduleLocationChange(p->loader()->completeURL(str).url(), p->loader()->outgoingReferrer(), true, userGesture);
2033       }
2034     }
2035
2036     return jsUndefined();
2037 }
2038
2039 JSValue* LocationProtoFuncReload::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2040 {
2041     if (!thisObj->inherits(&Location::info))
2042         return throwError(exec, TypeError);
2043     Location* location = static_cast<Location*>(thisObj);
2044     Frame* frame = location->frame();
2045     if (!frame)
2046         return jsUndefined();
2047
2048     Window* window = Window::retrieveWindow(frame);
2049     if (!window->isSafeScript(exec))
2050         return jsUndefined();
2051
2052     if (!frame->loader()->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2053       bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2054       frame->loader()->scheduleRefresh(userGesture);
2055     }
2056     return jsUndefined();
2057 }
2058
2059 JSValue* LocationProtoFuncAssign::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2060 {
2061     if (!thisObj->inherits(&Location::info))
2062         return throwError(exec, TypeError);
2063     Location* location = static_cast<Location*>(thisObj);
2064     Frame* frame = location->frame();
2065     if (!frame)
2066         return jsUndefined();
2067
2068     Frame *p = Window::retrieveActive(exec)->impl()->frame();
2069     if (p) {
2070         if (!p->loader()->shouldAllowNavigation(frame))
2071           return jsUndefined();
2072         const Window *window = Window::retrieveWindow(frame);
2073         DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(args[0]->toString(exec))).url();
2074         if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2075             bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2076             // We want a new history item if this JS was called via a user gesture
2077             frame->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
2078         }
2079     }
2080
2081     return jsUndefined();
2082 }
2083
2084 JSValue* LocationProtoFuncToString::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2085 {
2086     if (!thisObj->inherits(&Location::info))
2087         return throwError(exec, TypeError);
2088     Location* location = static_cast<Location*>(thisObj);
2089     Frame* frame = location->frame();
2090     if (!frame)
2091         return jsUndefined();
2092
2093     Window* window = Window::retrieveWindow(frame);
2094     if (!window->isSafeScript(exec))
2095         return jsUndefined();
2096
2097     if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
2098         return jsString();
2099
2100     if (!frame->loader()->url().hasPath())
2101         return jsString(frame->loader()->url().prettyURL() + "/");
2102     return jsString(frame->loader()->url().prettyURL());
2103 }
2104
2105 /////////////////////////////////////////////////////////////////////////////
2106
2107 PausedTimeouts::~PausedTimeouts()
2108 {
2109     PausedTimeout *array = m_array;
2110     if (!array)
2111         return;
2112     size_t count = m_length;
2113     for (size_t i = 0; i != count; ++i)
2114         delete array[i].action;
2115     delete [] array;
2116 }
2117
2118 void DOMWindowTimer::fired()
2119 {
2120     timerNestingLevel = m_nestingLevel;
2121     m_object->timerFired(this);
2122     timerNestingLevel = 0;
2123 }
2124
2125 } // namespace KJS
2126
2127 using namespace KJS;
2128
2129 namespace WebCore {
2130
2131 JSValue* toJS(ExecState*, DOMWindow* domWindow)
2132 {
2133     if (!domWindow)
2134         return jsNull();
2135     Frame* frame = domWindow->frame();
2136     if (!frame)
2137         return jsNull();
2138     return Window::retrieve(frame);
2139 }
2140     
2141 } // namespace WebCore