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