1 // -*- c-basic-offset: 4 -*-
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)
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.
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.
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
25 #include "kjs_window.h"
30 #include "DOMWindow.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
34 #include "ExceptionCode.h"
35 #include "FloatRect.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"
48 #include "JSHTMLAudioElementConstructor.h"
49 #include "JSHTMLCollection.h"
50 #include "JSHTMLOptionElementConstructor.h"
51 #include "JSMutationEvent.h"
53 #include "JSNodeFilter.h"
55 #include "JSXMLHttpRequest.h"
58 #include "PlatformScreen.h"
59 #include "PlugInInfoStore.h"
60 #include "RenderView.h"
61 #include "SecurityOrigin.h"
63 #include "WindowFeatures.h"
64 #include "htmlediting.h"
66 #include "kjs_events.h"
67 #include "kjs_navigator.h"
68 #include "kjs_proxy.h"
69 #include <wtf/MathExtras.h>
72 #include "JSXSLTProcessor.h"
75 using namespace WebCore;
76 using namespace EventNames;
80 static int lastUsedTimeoutId;
82 static int timerNestingLevel = 0;
83 const int cMaxTimerNestingLevel = 5;
84 const double cMinimumTimerInterval = 0.010;
86 struct WindowPrivate {
90 , m_returnValueSlot(0)
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;
105 class DOMWindowTimer : public TimerBase {
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()
115 int timeoutId() const { return m_timeoutId; }
117 int nestingLevel() const { return m_nestingLevel; }
118 void setNestingLevel(int n) { m_nestingLevel = n; }
120 ScheduledAction* action() const { return m_action; }
121 ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
124 virtual void fired();
129 ScheduledAction* m_action;
132 class PausedTimeout {
136 double nextFireInterval;
137 double repeatInterval;
138 ScheduledAction *action;
143 #include "kjs_window.lut.h"
147 ////////////////////// Window Object ////////////////////////
149 const ClassInfo Window::info = { "Window", 0, &WindowTable };
152 @begin WindowTable 118
153 # Warning, when adding a function to this object you need to add a case in Window::get
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
173 captureEvents &WindowProtoFuncNotImplemented::create DontDelete|Function 0
174 releaseEvents &WindowProtoFuncNotImplemented::create DontDelete|Function 0
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
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
218 Window::Window(DOMWindow* window)
220 , d(new WindowPrivate)
222 // Window destruction is not thread-safe because of
223 // the non-thread-safe WebCore structures it references.
224 Collector::collectOnMainThreadOnly(this);
231 // Clear any backpointers to the window
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();
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();
252 ScriptInterpreter* Window::interpreter() const
254 Frame* frame = impl()->frame();
258 return frame->scriptProxy()->interpreter();
261 Window *Window::retrieveWindow(Frame *f)
263 JSObject *o = retrieve(f)->getObject();
265 ASSERT(o || !f->settings() || !f->settings()->isJavaScriptEnabled());
266 return static_cast<Window *>(o);
269 Window *Window::retrieveActive(ExecState *exec)
271 JSValue *imp = exec->dynamicInterpreter()->globalObject();
273 return static_cast<Window*>(imp);
276 JSValue *Window::retrieve(Frame *p)
279 if (KJSProxy *proxy = p->scriptProxy())
280 return proxy->interpreter()->globalObject(); // the Global object is the "window"
282 return jsUndefined(); // This can happen with JS disabled on the domain of that window
285 Location *Window::location() const
288 d->loc = new Location(impl()->frame());
292 // reference our special objects during garbage collection
296 if (d->loc && !d->loc->marked())
300 static bool allowPopUp(ExecState *exec, Window *window)
302 Frame* frame = window->impl()->frame();
305 if (static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->wasRunByUserGesture())
307 Settings* settings = frame->settings();
308 return settings && settings->JavaScriptCanOpenWindowsAutomatically();
311 static HashMap<String, String> parseModalDialogFeatures(ExecState *exec, JSValue *featuresArg)
313 HashMap<String, String> map;
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) {
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 :
326 // null string for value means key without value
327 map.set(s.stripWhiteSpace().lower(), String());
329 String key = s.left(pos).stripWhiteSpace().lower();
330 String val = s.substring(pos + 1).stripWhiteSpace().lower();
331 int spacePos = val.find(' ');
333 val = val.left(spacePos);
341 static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false)
343 HashMap<String, String>::const_iterator it = features.find(key);
344 if (it == features.end())
346 const String& value = it->second;
347 return value.isNull() || value == "1" || value == "yes" || value == "on";
350 static float floatFeature(const HashMap<String, String> &features, const char *key, float min, float max, float defaultValue)
352 HashMap<String, String>::const_iterator it = features.find(key);
353 if (it == features.end())
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.
359 double d = it->second.toDouble(&ok);
360 if ((d == 0 && !ok) || isnan(d))
362 if (d < min || max <= min)
366 return static_cast<int>(d);
369 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
370 const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
372 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
374 ResourceRequest request;
376 request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
377 FrameLoadRequest frameRequest(request, frameName);
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 "".
387 Frame* newFrame = openerFrame->loader()->createWindow(frameRequest, windowFeatures, created);
391 newFrame->loader()->setOpener(openerFrame);
392 newFrame->loader()->setOpenedByDOM();
394 Window* newWindow = Window::retrieveWindow(newFrame);
397 newWindow->putDirect("dialogArguments", dialogArgs);
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();
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());
409 } else if (!url.isEmpty())
410 newFrame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
416 static bool canShowModalDialog(const Window *window)
418 Frame* frame = window->impl()->frame();
422 return frame->page()->chrome()->canRunModal();
425 static bool canShowModalDialogNow(const Window *window)
427 Frame* frame = window->impl()->frame();
431 return frame->page()->chrome()->canRunModalNow();
434 static JSValue* showModalDialog(ExecState* exec, Window* openerWindow, const List& args)
436 if (!canShowModalDialogNow(openerWindow) || !allowPopUp(exec, openerWindow))
437 return jsUndefined();
439 const HashMap<String, String> features = parseModalDialogFeatures(exec, args[2]);
441 bool trusted = false;
443 WindowFeatures wargs;
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();
454 return jsUndefined();
456 FloatRect screenRect = screenAvailableRect(frame->view());
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;
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;
468 if (boolFeature(features, "center", true)) {
470 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
474 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
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;
488 Frame* dialogFrame = createWindow(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args[0]), "", wargs, args[1]);
490 return jsUndefined();
492 Window* dialogWindow = Window::retrieveWindow(dialogFrame);
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);
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).
505 returnValue = dialogWindow->getDirect("returnValue");
507 return returnValue ? returnValue : jsUndefined();
510 JSValue *Window::getValueProperty(ExecState *exec, int token) const
512 ASSERT(impl()->frame());
516 if (!isSafeScript(exec))
517 return jsUndefined();
518 return jsUndefined(); // FIXME: implement this
520 if (!isSafeScript(exec))
521 return jsUndefined();
522 return getDOMExceptionConstructor(exec);
524 if (!isSafeScript(exec))
525 return jsUndefined();
527 return jsUndefined();
528 return toJS(exec, d->m_evt);
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);
544 if (!isSafeScript(exec))
545 return jsUndefined();
546 // FIXME: this property (and the few below) probably shouldn't create a new object every
548 return new ImageConstructorImp(exec, impl()->frame()->document());
550 if (!isSafeScript(exec))
551 return jsUndefined();
552 return new JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
554 if (!isSafeScript(exec))
555 return jsUndefined();
556 return new JSXMLHttpRequestConstructorImp(exec, impl()->frame()->document());
559 return new JSHTMLAudioElementConstructor(exec, impl()->frame()->document());
561 return jsUndefined();
565 if (!isSafeScript(exec))
566 return jsUndefined();
567 return new XSLTProcessorConstructorImp(exec);
570 return jsUndefined();
574 if (!isSafeScript(exec))
575 return jsUndefined();
579 return getListener(exec, abortEvent);
581 return getListener(exec, blurEvent);
583 return getListener(exec, changeEvent);
585 return getListener(exec, clickEvent);
587 return getListener(exec, dblclickEvent);
589 return getListener(exec, errorEvent);
591 return getListener(exec, focusEvent);
593 return getListener(exec, keydownEvent);
595 return getListener(exec, keypressEvent);
597 return getListener(exec, keyupEvent);
599 return getListener(exec, loadEvent);
601 return getListener(exec, mousedownEvent);
603 return getListener(exec, mousemoveEvent);
605 return getListener(exec, mouseoutEvent);
607 return getListener(exec, mouseoverEvent);
609 return getListener(exec, mouseupEvent);
610 case OnWindowMouseWheel:
611 return getListener(exec, mousewheelEvent);
613 return getListener(exec, resetEvent);
615 return getListener(exec,resizeEvent);
617 return getListener(exec,scrollEvent);
619 return getListener(exec,searchEvent);
621 return getListener(exec,selectEvent);
623 return getListener(exec,submitEvent);
625 return getListener(exec, beforeunloadEvent);
627 return getListener(exec, unloadEvent);
630 return jsUndefined();
633 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
635 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName)));
638 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
640 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index()));
643 JSValue *Window::namedItemGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
645 Window *thisObj = static_cast<Window *>(slot.slotBase());
646 Document *doc = thisObj->impl()->frame()->document();
647 ASSERT(thisObj->isSafeScript(exec) && doc && doc->isHTMLDocument());
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());
656 bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
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
663 if (impl()->frame()->tree()->child(propertyName)) {
664 slot.setCustom(this, childFrameGetter);
668 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
670 if (entry->attr & Function) {
671 if (entry->value.functionValue == &WindowProtoFuncShowModalDialog::create) {
672 if (!canShowModalDialog(this))
675 if (isSafeScript(exec))
676 slot.setStaticEntry(this, entry, staticFunctionGetter);
678 slot.setUndefined(this);
680 slot.setStaticEntry(this, entry, staticValueGetter<Window>);
684 // FIXME: Search the whole frame hierachy somewhere around here.
685 // We need to test the correct priority order.
687 // allow window[1] or parent[1] etc. (#56983)
689 unsigned i = propertyName.toArrayIndex(&ok);
690 if (ok && i < impl()->frame()->tree()->childCount()) {
691 slot.setCustomIndex(this, i, indexGetter);
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);
703 AtomicString atomicPropertyName = propertyName;
704 if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
705 slot.setCustom(this, namedItemGetter);
710 if (!isSafeScript(exec)) {
711 slot.setUndefined(this);
715 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
718 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
720 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
722 if (entry->attr & Function) {
723 if (isSafeScript(exec))
724 JSObject::put(exec, propertyName, value, attr);
727 if (entry->attr & ReadOnly)
730 switch (entry->value.intValue) {
732 Frame* p = Window::retrieveActive(exec)->impl()->frame();
734 DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(value->toString(exec))).url();
735 if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
736 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
737 // We want a new history item if this JS was called via a user gesture
738 impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
744 if (isSafeScript(exec))
745 setListener(exec, abortEvent,value);
748 if (isSafeScript(exec))
749 setListener(exec, blurEvent,value);
752 if (isSafeScript(exec))
753 setListener(exec, changeEvent,value);
756 if (isSafeScript(exec))
757 setListener(exec,clickEvent,value);
760 if (isSafeScript(exec))
761 setListener(exec, dblclickEvent,value);
764 if (isSafeScript(exec))
765 setListener(exec, errorEvent, value);
768 if (isSafeScript(exec))
769 setListener(exec,focusEvent,value);
772 if (isSafeScript(exec))
773 setListener(exec,keydownEvent,value);
776 if (isSafeScript(exec))
777 setListener(exec,keypressEvent,value);
780 if (isSafeScript(exec))
781 setListener(exec,keyupEvent,value);
784 if (isSafeScript(exec))
785 setListener(exec,loadEvent,value);
788 if (isSafeScript(exec))
789 setListener(exec,mousedownEvent,value);
792 if (isSafeScript(exec))
793 setListener(exec,mousemoveEvent,value);
796 if (isSafeScript(exec))
797 setListener(exec,mouseoutEvent,value);
800 if (isSafeScript(exec))
801 setListener(exec,mouseoverEvent,value);
804 if (isSafeScript(exec))
805 setListener(exec,mouseupEvent,value);
807 case OnWindowMouseWheel:
808 if (isSafeScript(exec))
809 setListener(exec, mousewheelEvent,value);
812 if (isSafeScript(exec))
813 setListener(exec,resetEvent,value);
816 if (isSafeScript(exec))
817 setListener(exec,resizeEvent,value);
820 if (isSafeScript(exec))
821 setListener(exec,scrollEvent,value);
824 if (isSafeScript(exec))
825 setListener(exec,searchEvent,value);
828 if (isSafeScript(exec))
829 setListener(exec,selectEvent,value);
832 if (isSafeScript(exec))
833 setListener(exec,submitEvent,value);
836 if (isSafeScript(exec))
837 setListener(exec, beforeunloadEvent, value);
840 if (isSafeScript(exec))
841 setListener(exec, unloadEvent, value);
847 if (isSafeScript(exec))
848 JSObject::put(exec, propertyName, value, attr);
851 static bool shouldLoadAsEmptyDocument(const KURL &url)
853 return url.protocol().lower() == "about" || url.isEmpty();
856 bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
858 if (origin == target)
861 Frame* originFrame = origin->frame();
862 Frame* targetFrame = target->frame();
864 // JS may be attempting to access the "window" object, which should be valid,
865 // even if the document hasn't been constructed yet. If the document doesn't
866 // exist yet allow JS to access the window object.
867 if (!targetFrame->document())
870 WebCore::Document *originDocument = originFrame->document();
871 WebCore::Document *targetDocument = targetFrame->document();
873 if (!targetDocument) {
877 WebCore::String targetDomain = targetDocument->domain();
879 // Always allow local pages to execute any JS.
880 if (targetDomain.isNull())
883 WebCore::String originDomain = originDocument->domain();
885 // if this document is being initially loaded as empty by its parent
886 // or opener, allow access from any document in the same domain as
887 // the parent or opener.
888 if (shouldLoadAsEmptyDocument(targetFrame->loader()->url())) {
889 Frame* ancestorFrame = targetFrame->loader()->opener() ? targetFrame->loader()->opener() : targetFrame->tree()->parent();
890 while (ancestorFrame && shouldLoadAsEmptyDocument(ancestorFrame->loader()->url()))
891 ancestorFrame = ancestorFrame->tree()->parent();
893 originDomain = ancestorFrame->document()->domain();
896 if (targetDomain == originDomain)
899 if (!originFrame->settings()->privateBrowsingEnabled()) {
900 if (Interpreter::shouldPrintExceptions())
901 printf("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n",
902 targetDocument->URL().latin1(), originDocument->URL().latin1());
903 String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n",
904 targetDocument->URL().latin1(), originDocument->URL().latin1());
905 if (Page* page = targetFrame->page())
906 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
912 bool Window::isSafeScript(ExecState *exec) const
914 Frame* frame = impl()->frame();
917 Frame* activeFrame = static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->frame();
920 if (activeFrame == frame)
923 WebCore::Document* thisDocument = frame->document();
925 // JS may be attempting to access the "window" object, which should be valid,
926 // even if the document hasn't been constructed yet. If the document doesn't
927 // exist yet allow JS to access the window object.
931 WebCore::Document* actDocument = activeFrame->document();
933 const SecurityOrigin& actSecurityOrigin = actDocument->securityOrigin();
934 const SecurityOrigin& thisSecurityOrigin = thisDocument->securityOrigin();
936 if (actSecurityOrigin.canAccess(thisSecurityOrigin))
939 if (!frame->settings()->privateBrowsingEnabled()) {
940 // FIXME: this error message should contain more specifics of why the same origin check has failed.
941 String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
942 thisDocument->URL().utf8().data(), actDocument->URL().utf8().data());
944 if (Interpreter::shouldPrintExceptions())
945 printf("%s", message.utf8().data());
947 if (Page* page = frame->page())
948 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
954 void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
956 if (!isSafeScript(exec))
958 Frame* frame = impl()->frame();
961 Document* doc = frame->document();
965 doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func,true));
968 JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
970 if (!isSafeScript(exec))
971 return jsUndefined();
972 Frame* frame = impl()->frame();
974 return jsUndefined();
975 Document* doc = frame->document();
977 return jsUndefined();
979 WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
980 if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
981 return static_cast<JSEventListener*>(listener)->listenerObj();
986 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
988 if (!val->isObject())
990 JSObject* object = static_cast<JSObject*>(val);
991 ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
992 return listeners.get(object);
995 JSEventListener *Window::findOrCreateJSEventListener(JSValue *val, bool html)
997 JSEventListener *listener = findJSEventListener(val, html);
1001 if (!val->isObject())
1003 JSObject *object = static_cast<JSObject *>(val);
1005 // Note that the JSEventListener constructor adds it to our jsEventListeners list
1006 return new JSEventListener(object, this, html);
1009 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1011 if (!val->isObject())
1013 JSObject* object = static_cast<JSObject*>(val);
1014 UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1015 return listeners.get(object);
1018 JSUnprotectedEventListener *Window::findOrCreateJSUnprotectedEventListener(JSValue *val, bool html)
1020 JSUnprotectedEventListener *listener = findJSUnprotectedEventListener(val, html);
1024 if (!val->isObject())
1026 JSObject *object = static_cast<JSObject *>(val);
1028 // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1029 return new JSUnprotectedEventListener(object, this, html);
1032 void Window::clearHelperObjectProperties()
1038 void Window::clear()
1042 if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1043 *d->m_returnValueSlot = getDirect("returnValue");
1047 clearHelperObjectProperties();
1048 setPrototype(JSDOMWindowPrototype::self()); // clear the prototype
1050 // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1052 if (Frame* frame = impl()->frame())
1053 frame->scriptProxy()->interpreter()->initGlobalObject();
1055 // there's likely to be lots of garbage now
1056 gcController().garbageCollectSoon();
1059 void Window::setCurrentEvent(Event *evt)
1064 static void setWindowFeature(const String& keyString, const String& valueString, WindowFeatures& windowFeatures)
1068 if (valueString.length() == 0 || // listing a key with no value is shorthand for key=yes
1069 valueString == "yes")
1072 value = valueString.toInt();
1074 if (keyString == "left" || keyString == "screenx") {
1075 windowFeatures.xSet = true;
1076 windowFeatures.x = value;
1077 } else if (keyString == "top" || keyString == "screeny") {
1078 windowFeatures.ySet = true;
1079 windowFeatures.y = value;
1080 } else if (keyString == "width" || keyString == "innerwidth") {
1081 windowFeatures.widthSet = true;
1082 windowFeatures.width = value;
1083 } else if (keyString == "height" || keyString == "innerheight") {
1084 windowFeatures.heightSet = true;
1085 windowFeatures.height = value;
1086 } else if (keyString == "menubar")
1087 windowFeatures.menuBarVisible = value;
1088 else if (keyString == "toolbar")
1089 windowFeatures.toolBarVisible = value;
1090 else if (keyString == "location")
1091 windowFeatures.locationBarVisible = value;
1092 else if (keyString == "status")
1093 windowFeatures.statusBarVisible = value;
1094 else if (keyString == "resizable")
1095 windowFeatures.resizable = value;
1096 else if (keyString == "fullscreen")
1097 windowFeatures.fullscreen = value;
1098 else if (keyString == "scrollbars")
1099 windowFeatures.scrollbarsVisible = value;
1102 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
1103 static bool isSeparator(::UChar c)
1105 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
1108 static void parseWindowFeatures(const String& features, WindowFeatures& windowFeatures)
1111 The IE rule is: all features except for channelmode and fullscreen default to YES, but
1112 if the user specifies a feature string, all features default to NO. (There is no public
1113 standard that applies to this method.)
1115 <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
1118 windowFeatures.dialog = false;
1119 windowFeatures.fullscreen = false;
1121 windowFeatures.xSet = false;
1122 windowFeatures.ySet = false;
1123 windowFeatures.widthSet = false;
1124 windowFeatures.heightSet = false;
1126 if (features.length() == 0) {
1127 windowFeatures.menuBarVisible = true;
1128 windowFeatures.statusBarVisible = true;
1129 windowFeatures.toolBarVisible = true;
1130 windowFeatures.locationBarVisible = true;
1131 windowFeatures.scrollbarsVisible = true;
1132 windowFeatures.resizable = true;
1137 windowFeatures.menuBarVisible = false;
1138 windowFeatures.statusBarVisible = false;
1139 windowFeatures.toolBarVisible = false;
1140 windowFeatures.locationBarVisible = false;
1141 windowFeatures.scrollbarsVisible = false;
1142 windowFeatures.resizable = false;
1144 // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
1145 int keyBegin, keyEnd;
1146 int valueBegin, valueEnd;
1149 int length = features.length();
1150 String buffer = features.lower();
1151 while (i < length) {
1152 // skip to first non-separator, but don't skip past the end of the string
1153 while (isSeparator(buffer[i])) {
1160 // skip to first separator
1161 while (!isSeparator(buffer[i]))
1165 // skip to first '=', but don't skip past a ',' or the end of the string
1166 while (buffer[i] != '=') {
1167 if (buffer[i] == ',' || i >= length)
1172 // skip to first non-separator, but don't skip past a ',' or the end of the string
1173 while (isSeparator(buffer[i])) {
1174 if (buffer[i] == ',' || i >= length)
1180 // skip to first separator
1181 while (!isSeparator(buffer[i]))
1185 ASSERT(i <= length);
1187 String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
1188 String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
1189 setWindowFeature(keyString, valueString, windowFeatures);
1193 // Explain the 4 things this function does.
1194 // 1) Validates the pending changes are not changing to NaN
1195 // 2) Constrains the window rect to no smaller than 100 in each dimension and no
1196 // bigger than the the float rect's dimensions.
1197 // 3) Constrain window rect to within the top and left boundaries of the screen rect
1198 // 4) Constraint the window rect to within the bottom and right boundaries of the
1200 // 5) Translate the window rect coordinates to be within the coordinate space of
1202 static void adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges)
1204 // Make sure we're in a valid state before adjusting dimensions
1205 ASSERT(!isnan(screen.x()) && !isnan(screen.y()) && !isnan(screen.width()) && !isnan(screen.height()) &&
1206 !isnan(window.x()) && !isnan(window.y()) && !isnan(window.width()) && !isnan(window.height()));
1208 // Update window values if they are not NaN
1209 if (!isnan(pendingChanges.x()))
1210 window.setX(pendingChanges.x());
1211 if (!isnan(pendingChanges.y()))
1212 window.setY(pendingChanges.y());
1213 if (!isnan(pendingChanges.width()))
1214 window.setWidth(pendingChanges.width());
1215 if (!isnan(pendingChanges.height()))
1216 window.setHeight(pendingChanges.height());
1218 // Resize the window to between 100 and the screen width and height if it's
1219 // outside of those ranges.
1220 window.setWidth(min(max(100.0f, window.width()), screen.width()));
1221 window.setHeight(min(max(100.0f, window.height()), screen.height()));
1223 // Constrain the window to the top and left of the screen if it's left or
1225 window.setX(max(window.x(), screen.x()));
1226 window.setY(max(window.y(), screen.y()));
1228 // Constrain the window to the bottom and right of the screen if it's past
1229 // the right or below it.
1230 window.setX(window.x() - max(0.0f, window.right() - screen.width()));
1231 window.setY(window.y() - max(0.0f, window.bottom() - screen.height()));
1233 // Adjust the window rect to be in the coordinate space of the screen rect
1234 window.setX(window.x() + screen.x());
1235 window.setY(window.y() + screen.y());
1238 JSValue* WindowProtoFuncAToB::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1240 if (!thisObj->inherits(&Window::info))
1241 return throwError(exec, TypeError);
1242 Window* window = static_cast<Window*>(thisObj);
1243 Frame* frame = window->impl()->frame();
1245 return jsUndefined();
1247 JSValue* v = args[0];
1248 UString s = v->toString(exec);
1250 if (args.size() < 1)
1251 return throwError(exec, SyntaxError, "Not enough arguments");
1255 setDOMException(exec, INVALID_CHARACTER_ERR);
1256 return jsUndefined();
1259 Vector<char> in(s.size());
1260 for (int i = 0; i < s.size(); ++i)
1261 in[i] = static_cast<char>(s.data()[i].unicode());
1264 if (!base64Decode(in, out))
1265 return throwError(exec, GeneralError, "Cannot decode base64");
1267 return jsString(String(out.data(), out.size()));
1270 JSValue* WindowProtoFuncBToA::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1272 if (!thisObj->inherits(&Window::info))
1273 return throwError(exec, TypeError);
1274 Window* window = static_cast<Window*>(thisObj);
1275 Frame* frame = window->impl()->frame();
1277 return jsUndefined();
1279 JSValue* v = args[0];
1280 UString s = v->toString(exec);
1282 if (args.size() < 1)
1283 return throwError(exec, SyntaxError, "Not enough arguments");
1287 setDOMException(exec, INVALID_CHARACTER_ERR);
1288 return jsUndefined();
1291 Vector<char> in(s.size());
1292 for (int i = 0; i < s.size(); ++i)
1293 in[i] = static_cast<char>(s.data()[i].unicode());
1296 base64Encode(in, out);
1298 return jsString(String(out.data(), out.size()));
1301 JSValue* WindowProtoFuncOpen::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1303 if (!thisObj->inherits(&Window::info))
1304 return throwError(exec, TypeError);
1305 Window* window = static_cast<Window*>(thisObj);
1306 Frame* frame = window->impl()->frame();
1308 return jsUndefined();
1310 Page* page = frame->page();
1312 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1313 AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1315 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1316 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1317 if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1318 return jsUndefined();
1320 // Get the target frame for the special cases of _top and _parent
1321 if (frameName == "_top")
1322 while (frame->tree()->parent())
1323 frame = frame->tree()->parent();
1324 else if (frameName == "_parent")
1325 if (frame->tree()->parent())
1326 frame = frame->tree()->parent();
1328 // In those cases, we can schedule a location change right now and return early
1329 if (frameName == "_top" || frameName == "_parent") {
1330 String completedURL;
1331 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1332 if (!urlString.isEmpty() && activeFrame)
1333 completedURL = activeFrame->document()->completeURL(urlString);
1335 const Window* window = Window::retrieveWindow(frame);
1336 if (!completedURL.isEmpty() && (!completedURL.startsWith("javascript:", false) || (window && window->isSafeScript(exec)))) {
1337 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1338 frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1340 return Window::retrieve(frame);
1343 // In the case of a named frame or a new window, we'll use the createWindow() helper
1344 WindowFeatures windowFeatures;
1345 String features = valueToStringWithUndefinedOrNullCheck(exec, args[2]);
1346 parseWindowFeatures(features, windowFeatures);
1347 FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1348 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1350 windowFeatures.x = windowRect.x();
1351 windowFeatures.y = windowRect.y();
1352 windowFeatures.height = windowRect.height();
1353 windowFeatures.width = windowRect.width();
1355 frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1358 return jsUndefined();
1360 return Window::retrieve(frame); // global object
1363 JSValue* WindowProtoFuncScrollBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1365 if (!thisObj->inherits(&Window::info))
1366 return throwError(exec, TypeError);
1367 Window* window = static_cast<Window*>(thisObj);
1368 Frame* frame = window->impl()->frame();
1370 return jsUndefined();
1372 FrameView *widget = frame->view();
1374 window->updateLayout();
1375 if(args.size() >= 2 && widget)
1376 widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1377 return jsUndefined();
1380 JSValue* WindowProtoFuncScrollTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1382 // Also the implementation for window.scroll()
1384 if (!thisObj->inherits(&Window::info))
1385 return throwError(exec, TypeError);
1386 Window* window = static_cast<Window*>(thisObj);
1387 Frame* frame = window->impl()->frame();
1389 return jsUndefined();
1391 FrameView *widget = frame->view();
1393 window->updateLayout();
1394 if (args.size() >= 2 && widget)
1395 widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1396 return jsUndefined();
1399 JSValue* WindowProtoFuncMoveBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1401 if (!thisObj->inherits(&Window::info))
1402 return throwError(exec, TypeError);
1403 Window* window = static_cast<Window*>(thisObj);
1404 Frame* frame = window->impl()->frame();
1406 return jsUndefined();
1408 Page* page = frame->page();
1410 if (args.size() >= 2 && page) {
1411 FloatRect fr = page->chrome()->windowRect();
1412 FloatRect update = fr;
1413 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1414 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1415 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1416 page->chrome()->setWindowRect(fr);
1418 return jsUndefined();
1421 JSValue* WindowProtoFuncMoveTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1423 if (!thisObj->inherits(&Window::info))
1424 return throwError(exec, TypeError);
1425 Window* window = static_cast<Window*>(thisObj);
1426 Frame* frame = window->impl()->frame();
1428 return jsUndefined();
1430 Page* page = frame->page();
1432 if (args.size() >= 2 && page) {
1433 FloatRect fr = page->chrome()->windowRect();
1434 FloatRect sr = screenAvailableRect(page->mainFrame()->view());
1435 fr.setLocation(sr.location());
1436 FloatRect update = fr;
1437 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1438 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1439 adjustWindowRect(sr, fr, update);
1440 page->chrome()->setWindowRect(fr);
1442 return jsUndefined();
1445 JSValue* WindowProtoFuncResizeBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1447 if (!thisObj->inherits(&Window::info))
1448 return throwError(exec, TypeError);
1449 Window* window = static_cast<Window*>(thisObj);
1450 Frame* frame = window->impl()->frame();
1452 return jsUndefined();
1454 Page* page = frame->page();
1456 if (args.size() >= 2 && page) {
1457 FloatRect fr = page->chrome()->windowRect();
1458 FloatSize dest = fr.size() + FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1459 FloatRect update(fr.location(), dest);
1460 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1461 page->chrome()->setWindowRect(fr);
1463 return jsUndefined();
1466 JSValue* WindowProtoFuncResizeTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1468 if (!thisObj->inherits(&Window::info))
1469 return throwError(exec, TypeError);
1470 Window* window = static_cast<Window*>(thisObj);
1471 Frame* frame = window->impl()->frame();
1473 return jsUndefined();
1475 Page* page = frame->page();
1477 if (args.size() >= 2 && page) {
1478 FloatRect fr = page->chrome()->windowRect();
1479 FloatSize dest = FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1480 FloatRect update(fr.location(), dest);
1481 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1482 page->chrome()->setWindowRect(fr);
1484 return jsUndefined();
1487 JSValue* WindowProtoFuncSetTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1489 if (!thisObj->inherits(&Window::info))
1490 return throwError(exec, TypeError);
1491 Window* window = static_cast<Window*>(thisObj);
1492 Frame* frame = window->impl()->frame();
1494 return jsUndefined();
1496 JSValue *v = args[0];
1497 UString s = v->toString(exec);
1499 if (!window->isSafeScript(exec))
1500 return jsUndefined();
1501 if (v->isString()) {
1502 int i = args[1]->toInt32(exec);
1503 int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1506 else if (v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1507 JSValue *func = args[0];
1508 int i = args[1]->toInt32(exec);
1511 args.getSlice(2, argsTail);
1513 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, true /*single shot*/);
1517 return jsUndefined();
1520 JSValue* WindowProtoFuncClearTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1522 // Also the implementation for window.clearInterval()
1524 if (!thisObj->inherits(&Window::info))
1525 return throwError(exec, TypeError);
1526 Window* window = static_cast<Window*>(thisObj);
1527 Frame* frame = window->impl()->frame();
1529 return jsUndefined();
1531 JSValue *v = args[0];
1533 if (!window->isSafeScript(exec))
1534 return jsUndefined();
1535 (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1536 return jsUndefined();
1539 JSValue* WindowProtoFuncSetInterval::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1541 if (!thisObj->inherits(&Window::info))
1542 return throwError(exec, TypeError);
1543 Window* window = static_cast<Window*>(thisObj);
1544 Frame* frame = window->impl()->frame();
1546 return jsUndefined();
1548 JSValue *v = args[0];
1549 UString s = v->toString(exec);
1551 if (!window->isSafeScript(exec))
1552 return jsUndefined();
1553 if (args.size() >= 2 && v->isString()) {
1554 int i = args[1]->toInt32(exec);
1555 int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1558 else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1559 JSValue *func = args[0];
1560 int i = args[1]->toInt32(exec);
1563 args.getSlice(2, argsTail);
1565 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, false);
1569 return jsUndefined();
1573 JSValue* WindowProtoFuncAddEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1575 if (!thisObj->inherits(&Window::info))
1576 return throwError(exec, TypeError);
1577 Window* window = static_cast<Window*>(thisObj);
1578 Frame* frame = window->impl()->frame();
1580 return jsUndefined();
1582 if (!window->isSafeScript(exec))
1583 return jsUndefined();
1584 if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1]))
1585 if (Document *doc = frame->document())
1586 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1587 return jsUndefined();
1590 JSValue* WindowProtoFuncRemoveEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1592 if (!thisObj->inherits(&Window::info))
1593 return throwError(exec, TypeError);
1594 Window* window = static_cast<Window*>(thisObj);
1595 Frame* frame = window->impl()->frame();
1597 return jsUndefined();
1599 if (!window->isSafeScript(exec))
1600 return jsUndefined();
1601 if (JSEventListener* listener = window->findJSEventListener(args[1]))
1602 if (Document *doc = frame->document())
1603 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1604 return jsUndefined();
1607 JSValue* WindowProtoFuncShowModalDialog::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1609 if (!thisObj->inherits(&Window::info))
1610 return throwError(exec, TypeError);
1611 Window* window = static_cast<Window*>(thisObj);
1612 Frame* frame = window->impl()->frame();
1614 return jsUndefined();
1616 return showModalDialog(exec, window, args);
1619 JSValue* WindowProtoFuncNotImplemented::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1621 if (!thisObj->inherits(&Window::info))
1622 return throwError(exec, TypeError);
1623 Window* window = static_cast<Window*>(thisObj);
1624 Frame* frame = window->impl()->frame();
1626 return jsUndefined();
1628 // If anyone implements these, they need the safe script security check.
1629 if (!window->isSafeScript(exec))
1630 return jsUndefined();
1632 return jsUndefined();
1635 void Window::updateLayout() const
1637 Frame* frame = impl()->frame();
1640 WebCore::Document* docimpl = frame->document();
1642 docimpl->updateLayoutIgnorePendingStylesheets();
1645 void Window::setReturnValueSlot(JSValue** slot)
1647 d->m_returnValueSlot = slot;
1650 ////////////////////// ScheduledAction ////////////////////////
1652 void ScheduledAction::execute(Window* window)
1654 RefPtr<Frame> frame = window->impl()->frame();
1658 KJSProxy* scriptProxy = frame->scriptProxy();
1662 RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
1664 interpreter->setProcessingTimerCallback(true);
1666 if (JSValue* func = m_func.get()) {
1668 if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
1669 ExecState* exec = interpreter->globalExec();
1670 ASSERT(window == interpreter->globalObject());
1673 size_t size = m_args.size();
1674 for (size_t i = 0; i < size; ++i) {
1675 args.append(m_args[i]);
1678 interpreter->startTimeoutCheck();
1679 static_cast<JSObject*>(func)->call(exec, window, args);
1680 interpreter->stopTimeoutCheck();
1681 if (exec->hadException()) {
1682 JSObject* exception = exec->exception()->toObject(exec);
1683 exec->clearException();
1684 String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
1685 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1686 if (Interpreter::shouldPrintExceptions())
1687 printf("(timer):%s\n", message.utf8().data());
1688 if (Page* page = frame->page())
1689 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
1693 frame->loader()->executeScript(m_code);
1695 // Update our document's rendering following the execution of the timeout callback.
1696 // FIXME: Why not use updateDocumentsRendering to update rendering of all documents?
1697 // FIXME: Is this really the right point to do the update? We need a place that works
1698 // for all possible entry points that might possibly execute script, but this seems
1699 // to be a bit too low-level.
1700 if (Document* doc = frame->document())
1701 doc->updateRendering();
1703 interpreter->setProcessingTimerCallback(false);
1706 ////////////////////// timeouts ////////////////////////
1708 void Window::clearAllTimeouts()
1710 deleteAllValues(d->m_timeouts);
1711 d->m_timeouts.clear();
1714 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1716 int timeoutId = ++lastUsedTimeoutId;
1718 // avoid wraparound going negative on us
1722 int nestLevel = timerNestingLevel + 1;
1723 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1724 ASSERT(!d->m_timeouts.get(timeoutId));
1725 d->m_timeouts.set(timeoutId, timer);
1726 // Use a minimum interval of 10 ms to match other browsers, but only once we've
1727 // nested enough to notice that we're repeating.
1728 // Faster timers might be "better", but they're incompatible.
1729 double interval = max(0.001, t * 0.001);
1730 if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1731 interval = cMinimumTimerInterval;
1733 timer->startOneShot(interval);
1735 timer->startRepeating(interval);
1739 ScheduledAction::ScheduledAction(JSValue* func, const List& args)
1742 List::const_iterator end = args.end();
1743 for (List::const_iterator it = args.begin(); it != end; ++it)
1747 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1749 return installTimeout(new ScheduledAction(handler), t, singleShot);
1752 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1754 return installTimeout(new ScheduledAction(func, args), t, singleShot);
1757 PausedTimeouts* Window::pauseTimeouts()
1759 size_t count = d->m_timeouts.size();
1763 PausedTimeout* t = new PausedTimeout [count];
1764 PausedTimeouts* result = new PausedTimeouts(t, count);
1766 WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1767 for (size_t i = 0; i != count; ++i, ++it) {
1768 int timeoutId = it->first;
1769 DOMWindowTimer* timer = it->second;
1770 t[i].timeoutId = timeoutId;
1771 t[i].nestingLevel = timer->nestingLevel();
1772 t[i].nextFireInterval = timer->nextFireInterval();
1773 t[i].repeatInterval = timer->repeatInterval();
1774 t[i].action = timer->takeAction();
1776 ASSERT(it == d->m_timeouts.end());
1778 deleteAllValues(d->m_timeouts);
1779 d->m_timeouts.clear();
1784 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1788 size_t count = timeouts->numTimeouts();
1789 PausedTimeout* array = timeouts->takeTimeouts();
1790 for (size_t i = 0; i != count; ++i) {
1791 int timeoutId = array[i].timeoutId;
1792 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1793 d->m_timeouts.set(timeoutId, timer);
1794 timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1799 void Window::clearTimeout(int timeoutId, bool delAction)
1801 // timeout IDs have to be positive, and 0 and -1 are unsafe to
1802 // even look up since they are the empty and deleted value
1807 delete d->m_timeouts.take(timeoutId);
1810 void Window::timerFired(DOMWindowTimer* timer)
1812 // Simple case for non-one-shot timers.
1813 if (timer->isActive()) {
1814 int timeoutId = timer->timeoutId();
1816 timer->action()->execute(this);
1817 if (d->m_timeouts.contains(timeoutId) && timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1818 timer->setNestingLevel(timer->nestingLevel() + 1);
1819 if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1820 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1825 // Delete timer before executing the action for one-shot timers.
1826 ScheduledAction* action = timer->takeAction();
1827 d->m_timeouts.remove(timer->timeoutId());
1829 action->execute(this);
1835 void Window::disconnectFrame()
1839 d->loc->m_frame = 0;
1842 Window::ListenersMap& Window::jsEventListeners()
1844 return d->jsEventListeners;
1847 Window::ListenersMap& Window::jsHTMLEventListeners()
1849 return d->jsHTMLEventListeners;
1852 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1854 return d->jsUnprotectedEventListeners;
1857 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1859 return d->jsUnprotectedHTMLEventListeners;
1862 ////////////////////// Location Object ////////////////////////
1864 const ClassInfo Location::info = { "Location", 0, &LocationTable };
1866 @begin LocationTable 12
1867 assign &LocationProtoFuncAssign::create DontDelete|Function 1
1868 hash Location::Hash DontDelete
1869 host Location::Host DontDelete
1870 hostname Location::Hostname DontDelete
1871 href Location::Href DontDelete
1872 pathname Location::Pathname DontDelete
1873 port Location::Port DontDelete
1874 protocol Location::Protocol DontDelete
1875 search Location::Search DontDelete
1876 toString &LocationProtoFuncToString::create DontEnum|DontDelete|Function 0
1877 replace &LocationProtoFuncReplace::create DontDelete|Function 1
1878 reload &LocationProtoFuncReload::create DontDelete|Function 0
1882 Location::Location(Frame *p) : m_frame(p)
1886 JSValue *Location::getValueProperty(ExecState *exec, int token) const
1888 KURL url = m_frame->loader()->url();
1891 return jsString(url.ref().isNull() ? "" : "#" + url.ref());
1893 // Note: this is the IE spec. The NS spec swaps the two, it says
1894 // "The hostname property is the concatenation of the host and port properties, separated by a colon."
1896 UString str = url.host();
1898 str += ":" + String::number((int)url.port());
1899 return jsString(str);
1902 return jsString(url.host());
1905 return jsString(url.prettyURL() + "/");
1906 return jsString(url.prettyURL());
1908 return jsString(url.path().isEmpty() ? "/" : url.path());
1910 return jsString(url.port() ? String::number((int)url.port()) : "");
1912 return jsString(url.protocol() + ":");
1914 return jsString(url.query());
1917 return jsUndefined();
1921 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
1926 const Window* window = Window::retrieveWindow(m_frame);
1928 const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
1929 if (!entry || !(entry->attr & KJS::Function) || (entry->value.functionValue != &LocationProtoFuncReplace::create
1930 && entry->value.functionValue != &LocationProtoFuncReload::create
1931 && entry->value.functionValue != &LocationProtoFuncAssign::create)) {
1932 if (!window || !window->isSafeScript(exec)) {
1933 slot.setUndefined(this);
1938 return getStaticPropertySlot<Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
1941 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
1946 DeprecatedString str = v->toString(exec);
1947 KURL url = m_frame->loader()->url();
1948 const Window* window = Window::retrieveWindow(m_frame);
1949 bool sameDomainAccess = window && window->isSafeScript(exec);
1951 const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
1954 // cross-domain access to the location is allowed when assigning the whole location,
1955 // but not when assigning the individual pieces, since that might inadvertently
1956 // disclose other parts of the original location.
1957 if (entry->value.intValue != Href && !sameDomainAccess)
1960 switch (entry->value.intValue) {
1962 Frame* frame = Window::retrieveActive(exec)->impl()->frame();
1964 url = frame->loader()->completeURL(str).url();
1970 if (str.startsWith("#"))
1973 if (url.ref() == str)
1980 url.setHostAndPort(str);
1990 url.setPort(str.toUInt());
1993 url.setProtocol(str);
1999 // Disallow changing other properties in LocationTable. e.g., "window.location.toString = ...".
2000 // <http://bugs.webkit.org/show_bug.cgi?id=12720>
2004 if (sameDomainAccess)
2005 JSObject::put(exec, p, v, attr);
2009 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
2010 if (!url.url().startsWith("javascript:", false) || sameDomainAccess) {
2011 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2012 m_frame->loader()->scheduleLocationChange(url.url(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
2016 JSValue* LocationProtoFuncReplace::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2018 if (!thisObj->inherits(&Location::info))
2019 return throwError(exec, TypeError);
2020 Location* location = static_cast<Location*>(thisObj);
2021 Frame* frame = location->frame();
2023 return jsUndefined();
2025 DeprecatedString str = args[0]->toString(exec);
2026 Frame* p = Window::retrieveActive(exec)->impl()->frame();
2028 const Window* window = Window::retrieveWindow(frame);
2029 if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2030 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2031 frame->loader()->scheduleLocationChange(p->loader()->completeURL(str).url(), p->loader()->outgoingReferrer(), true, userGesture);
2035 return jsUndefined();
2038 JSValue* LocationProtoFuncReload::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2040 if (!thisObj->inherits(&Location::info))
2041 return throwError(exec, TypeError);
2042 Location* location = static_cast<Location*>(thisObj);
2043 Frame* frame = location->frame();
2045 return jsUndefined();
2047 Window* window = Window::retrieveWindow(frame);
2048 if (!window->isSafeScript(exec))
2049 return jsUndefined();
2051 if (!frame->loader()->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2052 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2053 frame->loader()->scheduleRefresh(userGesture);
2055 return jsUndefined();
2058 JSValue* LocationProtoFuncAssign::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2060 if (!thisObj->inherits(&Location::info))
2061 return throwError(exec, TypeError);
2062 Location* location = static_cast<Location*>(thisObj);
2063 Frame* frame = location->frame();
2065 return jsUndefined();
2067 Window* window = Window::retrieveWindow(frame);
2068 if (!window->isSafeScript(exec))
2069 return jsUndefined();
2071 Frame *p = Window::retrieveActive(exec)->impl()->frame();
2073 const Window *window = Window::retrieveWindow(frame);
2074 DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(args[0]->toString(exec))).url();
2075 if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2076 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2077 // We want a new history item if this JS was called via a user gesture
2078 frame->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
2082 return jsUndefined();
2085 JSValue* LocationProtoFuncToString::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2087 if (!thisObj->inherits(&Location::info))
2088 return throwError(exec, TypeError);
2089 Location* location = static_cast<Location*>(thisObj);
2090 Frame* frame = location->frame();
2092 return jsUndefined();
2094 Window* window = Window::retrieveWindow(frame);
2095 if (!window->isSafeScript(exec))
2096 return jsUndefined();
2098 if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
2101 if (!frame->loader()->url().hasPath())
2102 return jsString(frame->loader()->url().prettyURL() + "/");
2103 return jsString(frame->loader()->url().prettyURL());
2106 /////////////////////////////////////////////////////////////////////////////
2108 PausedTimeouts::~PausedTimeouts()
2110 PausedTimeout *array = m_array;
2113 size_t count = m_length;
2114 for (size_t i = 0; i != count; ++i)
2115 delete array[i].action;
2119 void DOMWindowTimer::fired()
2121 timerNestingLevel = m_nestingLevel;
2122 m_object->timerFired(this);
2123 timerNestingLevel = 0;
2128 using namespace KJS;
2132 JSValue* toJS(ExecState*, DOMWindow* domWindow)
2136 Frame* frame = domWindow->frame();
2139 return Window::retrieve(frame);
2142 } // namespace WebCore