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 // FIXME: this error message should contain more specifics of why the same origin check has failed.
940 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",
941 thisDocument->URL().utf8().data(), actDocument->URL().utf8().data());
943 if (Interpreter::shouldPrintExceptions())
944 printf("%s", message.utf8().data());
946 if (Page* page = frame->page())
947 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
952 void Window::setListener(ExecState *exec, const AtomicString &eventType, JSValue *func)
954 if (!isSafeScript(exec))
956 Frame* frame = impl()->frame();
959 Document* doc = frame->document();
963 doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func,true));
966 JSValue *Window::getListener(ExecState *exec, const AtomicString &eventType) const
968 if (!isSafeScript(exec))
969 return jsUndefined();
970 Frame* frame = impl()->frame();
972 return jsUndefined();
973 Document* doc = frame->document();
975 return jsUndefined();
977 WebCore::EventListener *listener = doc->getHTMLWindowEventListener(eventType);
978 if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
979 return static_cast<JSEventListener*>(listener)->listenerObj();
984 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
986 if (!val->isObject())
988 JSObject* object = static_cast<JSObject*>(val);
989 ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
990 return listeners.get(object);
993 JSEventListener *Window::findOrCreateJSEventListener(JSValue *val, bool html)
995 JSEventListener *listener = findJSEventListener(val, html);
999 if (!val->isObject())
1001 JSObject *object = static_cast<JSObject *>(val);
1003 // Note that the JSEventListener constructor adds it to our jsEventListeners list
1004 return new JSEventListener(object, this, html);
1007 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1009 if (!val->isObject())
1011 JSObject* object = static_cast<JSObject*>(val);
1012 UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1013 return listeners.get(object);
1016 JSUnprotectedEventListener *Window::findOrCreateJSUnprotectedEventListener(JSValue *val, bool html)
1018 JSUnprotectedEventListener *listener = findJSUnprotectedEventListener(val, html);
1022 if (!val->isObject())
1024 JSObject *object = static_cast<JSObject *>(val);
1026 // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1027 return new JSUnprotectedEventListener(object, this, html);
1030 void Window::clearHelperObjectProperties()
1036 void Window::clear()
1040 if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1041 *d->m_returnValueSlot = getDirect("returnValue");
1045 clearHelperObjectProperties();
1046 setPrototype(JSDOMWindowPrototype::self()); // clear the prototype
1048 // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1050 if (Frame* frame = impl()->frame())
1051 frame->scriptProxy()->interpreter()->initGlobalObject();
1053 // there's likely to be lots of garbage now
1054 gcController().garbageCollectSoon();
1057 void Window::setCurrentEvent(Event *evt)
1062 static void setWindowFeature(const String& keyString, const String& valueString, WindowFeatures& windowFeatures)
1066 if (valueString.length() == 0 || // listing a key with no value is shorthand for key=yes
1067 valueString == "yes")
1070 value = valueString.toInt();
1072 if (keyString == "left" || keyString == "screenx") {
1073 windowFeatures.xSet = true;
1074 windowFeatures.x = value;
1075 } else if (keyString == "top" || keyString == "screeny") {
1076 windowFeatures.ySet = true;
1077 windowFeatures.y = value;
1078 } else if (keyString == "width" || keyString == "innerwidth") {
1079 windowFeatures.widthSet = true;
1080 windowFeatures.width = value;
1081 } else if (keyString == "height" || keyString == "innerheight") {
1082 windowFeatures.heightSet = true;
1083 windowFeatures.height = value;
1084 } else if (keyString == "menubar")
1085 windowFeatures.menuBarVisible = value;
1086 else if (keyString == "toolbar")
1087 windowFeatures.toolBarVisible = value;
1088 else if (keyString == "location")
1089 windowFeatures.locationBarVisible = value;
1090 else if (keyString == "status")
1091 windowFeatures.statusBarVisible = value;
1092 else if (keyString == "resizable")
1093 windowFeatures.resizable = value;
1094 else if (keyString == "fullscreen")
1095 windowFeatures.fullscreen = value;
1096 else if (keyString == "scrollbars")
1097 windowFeatures.scrollbarsVisible = value;
1100 // Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
1101 static bool isSeparator(::UChar c)
1103 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
1106 static void parseWindowFeatures(const String& features, WindowFeatures& windowFeatures)
1109 The IE rule is: all features except for channelmode and fullscreen default to YES, but
1110 if the user specifies a feature string, all features default to NO. (There is no public
1111 standard that applies to this method.)
1113 <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
1116 windowFeatures.dialog = false;
1117 windowFeatures.fullscreen = false;
1119 windowFeatures.xSet = false;
1120 windowFeatures.ySet = false;
1121 windowFeatures.widthSet = false;
1122 windowFeatures.heightSet = false;
1124 if (features.length() == 0) {
1125 windowFeatures.menuBarVisible = true;
1126 windowFeatures.statusBarVisible = true;
1127 windowFeatures.toolBarVisible = true;
1128 windowFeatures.locationBarVisible = true;
1129 windowFeatures.scrollbarsVisible = true;
1130 windowFeatures.resizable = true;
1135 windowFeatures.menuBarVisible = false;
1136 windowFeatures.statusBarVisible = false;
1137 windowFeatures.toolBarVisible = false;
1138 windowFeatures.locationBarVisible = false;
1139 windowFeatures.scrollbarsVisible = false;
1140 windowFeatures.resizable = false;
1142 // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
1143 int keyBegin, keyEnd;
1144 int valueBegin, valueEnd;
1147 int length = features.length();
1148 String buffer = features.lower();
1149 while (i < length) {
1150 // skip to first non-separator, but don't skip past the end of the string
1151 while (isSeparator(buffer[i])) {
1158 // skip to first separator
1159 while (!isSeparator(buffer[i]))
1163 // skip to first '=', but don't skip past a ',' or the end of the string
1164 while (buffer[i] != '=') {
1165 if (buffer[i] == ',' || i >= length)
1170 // skip to first non-separator, but don't skip past a ',' or the end of the string
1171 while (isSeparator(buffer[i])) {
1172 if (buffer[i] == ',' || i >= length)
1178 // skip to first separator
1179 while (!isSeparator(buffer[i]))
1183 ASSERT(i <= length);
1185 String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
1186 String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
1187 setWindowFeature(keyString, valueString, windowFeatures);
1191 // Explain the 4 things this function does.
1192 // 1) Validates the pending changes are not changing to NaN
1193 // 2) Constrains the window rect to no smaller than 100 in each dimension and no
1194 // bigger than the the float rect's dimensions.
1195 // 3) Constrain window rect to within the top and left boundaries of the screen rect
1196 // 4) Constraint the window rect to within the bottom and right boundaries of the
1198 // 5) Translate the window rect coordinates to be within the coordinate space of
1200 static void adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges)
1202 // Make sure we're in a valid state before adjusting dimensions
1203 ASSERT(!isnan(screen.x()) && !isnan(screen.y()) && !isnan(screen.width()) && !isnan(screen.height()) &&
1204 !isnan(window.x()) && !isnan(window.y()) && !isnan(window.width()) && !isnan(window.height()));
1206 // Update window values if they are not NaN
1207 if (!isnan(pendingChanges.x()))
1208 window.setX(pendingChanges.x());
1209 if (!isnan(pendingChanges.y()))
1210 window.setY(pendingChanges.y());
1211 if (!isnan(pendingChanges.width()))
1212 window.setWidth(pendingChanges.width());
1213 if (!isnan(pendingChanges.height()))
1214 window.setHeight(pendingChanges.height());
1216 // Resize the window to between 100 and the screen width and height if it's
1217 // outside of those ranges.
1218 window.setWidth(min(max(100.0f, window.width()), screen.width()));
1219 window.setHeight(min(max(100.0f, window.height()), screen.height()));
1221 // Constrain the window to the top and left of the screen if it's left or
1223 window.setX(max(window.x(), screen.x()));
1224 window.setY(max(window.y(), screen.y()));
1226 // Constrain the window to the bottom and right of the screen if it's past
1227 // the right or below it.
1228 window.setX(window.x() - max(0.0f, window.right() - screen.width()));
1229 window.setY(window.y() - max(0.0f, window.bottom() - screen.height()));
1231 // Adjust the window rect to be in the coordinate space of the screen rect
1232 window.setX(window.x() + screen.x());
1233 window.setY(window.y() + screen.y());
1236 JSValue* WindowProtoFuncAToB::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1238 if (!thisObj->inherits(&Window::info))
1239 return throwError(exec, TypeError);
1240 Window* window = static_cast<Window*>(thisObj);
1241 Frame* frame = window->impl()->frame();
1243 return jsUndefined();
1245 JSValue* v = args[0];
1246 UString s = v->toString(exec);
1248 if (args.size() < 1)
1249 return throwError(exec, SyntaxError, "Not enough arguments");
1253 setDOMException(exec, INVALID_CHARACTER_ERR);
1254 return jsUndefined();
1257 Vector<char> in(s.size());
1258 for (int i = 0; i < s.size(); ++i)
1259 in[i] = static_cast<char>(s.data()[i].unicode());
1262 if (!base64Decode(in, out))
1263 return throwError(exec, GeneralError, "Cannot decode base64");
1265 return jsString(String(out.data(), out.size()));
1268 JSValue* WindowProtoFuncBToA::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1270 if (!thisObj->inherits(&Window::info))
1271 return throwError(exec, TypeError);
1272 Window* window = static_cast<Window*>(thisObj);
1273 Frame* frame = window->impl()->frame();
1275 return jsUndefined();
1277 JSValue* v = args[0];
1278 UString s = v->toString(exec);
1280 if (args.size() < 1)
1281 return throwError(exec, SyntaxError, "Not enough arguments");
1285 setDOMException(exec, INVALID_CHARACTER_ERR);
1286 return jsUndefined();
1289 Vector<char> in(s.size());
1290 for (int i = 0; i < s.size(); ++i)
1291 in[i] = static_cast<char>(s.data()[i].unicode());
1294 base64Encode(in, out);
1296 return jsString(String(out.data(), out.size()));
1299 JSValue* WindowProtoFuncOpen::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1301 if (!thisObj->inherits(&Window::info))
1302 return throwError(exec, TypeError);
1303 Window* window = static_cast<Window*>(thisObj);
1304 Frame* frame = window->impl()->frame();
1306 return jsUndefined();
1308 Page* page = frame->page();
1310 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1311 AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1313 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1314 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1315 if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1316 return jsUndefined();
1318 // Get the target frame for the special cases of _top and _parent
1319 if (frameName == "_top")
1320 while (frame->tree()->parent())
1321 frame = frame->tree()->parent();
1322 else if (frameName == "_parent")
1323 if (frame->tree()->parent())
1324 frame = frame->tree()->parent();
1326 // In those cases, we can schedule a location change right now and return early
1327 if (frameName == "_top" || frameName == "_parent") {
1328 String completedURL;
1329 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1330 if (!urlString.isEmpty() && activeFrame)
1331 completedURL = activeFrame->document()->completeURL(urlString);
1333 const Window* window = Window::retrieveWindow(frame);
1334 if (!completedURL.isEmpty() && (!completedURL.startsWith("javascript:", false) || (window && window->isSafeScript(exec)))) {
1335 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1336 frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1338 return Window::retrieve(frame);
1341 // In the case of a named frame or a new window, we'll use the createWindow() helper
1342 WindowFeatures windowFeatures;
1343 String features = valueToStringWithUndefinedOrNullCheck(exec, args[2]);
1344 parseWindowFeatures(features, windowFeatures);
1345 FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1346 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1348 windowFeatures.x = windowRect.x();
1349 windowFeatures.y = windowRect.y();
1350 windowFeatures.height = windowRect.height();
1351 windowFeatures.width = windowRect.width();
1353 frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1356 return jsUndefined();
1358 return Window::retrieve(frame); // global object
1361 JSValue* WindowProtoFuncScrollBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1363 if (!thisObj->inherits(&Window::info))
1364 return throwError(exec, TypeError);
1365 Window* window = static_cast<Window*>(thisObj);
1366 Frame* frame = window->impl()->frame();
1368 return jsUndefined();
1370 FrameView *widget = frame->view();
1372 window->updateLayout();
1373 if(args.size() >= 2 && widget)
1374 widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1375 return jsUndefined();
1378 JSValue* WindowProtoFuncScrollTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1380 // Also the implementation for window.scroll()
1382 if (!thisObj->inherits(&Window::info))
1383 return throwError(exec, TypeError);
1384 Window* window = static_cast<Window*>(thisObj);
1385 Frame* frame = window->impl()->frame();
1387 return jsUndefined();
1389 FrameView *widget = frame->view();
1391 window->updateLayout();
1392 if (args.size() >= 2 && widget)
1393 widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1394 return jsUndefined();
1397 JSValue* WindowProtoFuncMoveBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1399 if (!thisObj->inherits(&Window::info))
1400 return throwError(exec, TypeError);
1401 Window* window = static_cast<Window*>(thisObj);
1402 Frame* frame = window->impl()->frame();
1404 return jsUndefined();
1406 Page* page = frame->page();
1408 if (args.size() >= 2 && page) {
1409 FloatRect fr = page->chrome()->windowRect();
1410 FloatRect update = fr;
1411 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1412 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1413 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1414 page->chrome()->setWindowRect(fr);
1416 return jsUndefined();
1419 JSValue* WindowProtoFuncMoveTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1421 if (!thisObj->inherits(&Window::info))
1422 return throwError(exec, TypeError);
1423 Window* window = static_cast<Window*>(thisObj);
1424 Frame* frame = window->impl()->frame();
1426 return jsUndefined();
1428 Page* page = frame->page();
1430 if (args.size() >= 2 && page) {
1431 FloatRect fr = page->chrome()->windowRect();
1432 FloatRect sr = screenAvailableRect(page->mainFrame()->view());
1433 fr.setLocation(sr.location());
1434 FloatRect update = fr;
1435 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1436 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1437 adjustWindowRect(sr, fr, update);
1438 page->chrome()->setWindowRect(fr);
1440 return jsUndefined();
1443 JSValue* WindowProtoFuncResizeBy::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1445 if (!thisObj->inherits(&Window::info))
1446 return throwError(exec, TypeError);
1447 Window* window = static_cast<Window*>(thisObj);
1448 Frame* frame = window->impl()->frame();
1450 return jsUndefined();
1452 Page* page = frame->page();
1454 if (args.size() >= 2 && page) {
1455 FloatRect fr = page->chrome()->windowRect();
1456 FloatSize dest = fr.size() + FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1457 FloatRect update(fr.location(), dest);
1458 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1459 page->chrome()->setWindowRect(fr);
1461 return jsUndefined();
1464 JSValue* WindowProtoFuncResizeTo::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1466 if (!thisObj->inherits(&Window::info))
1467 return throwError(exec, TypeError);
1468 Window* window = static_cast<Window*>(thisObj);
1469 Frame* frame = window->impl()->frame();
1471 return jsUndefined();
1473 Page* page = frame->page();
1475 if (args.size() >= 2 && page) {
1476 FloatRect fr = page->chrome()->windowRect();
1477 FloatSize dest = FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1478 FloatRect update(fr.location(), dest);
1479 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1480 page->chrome()->setWindowRect(fr);
1482 return jsUndefined();
1485 JSValue* WindowProtoFuncSetTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1487 if (!thisObj->inherits(&Window::info))
1488 return throwError(exec, TypeError);
1489 Window* window = static_cast<Window*>(thisObj);
1490 Frame* frame = window->impl()->frame();
1492 return jsUndefined();
1494 JSValue *v = args[0];
1495 UString s = v->toString(exec);
1497 if (!window->isSafeScript(exec))
1498 return jsUndefined();
1499 if (v->isString()) {
1500 int i = args[1]->toInt32(exec);
1501 int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1504 else if (v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1505 JSValue *func = args[0];
1506 int i = args[1]->toInt32(exec);
1509 args.getSlice(2, argsTail);
1511 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, true /*single shot*/);
1515 return jsUndefined();
1518 JSValue* WindowProtoFuncClearTimeout::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1520 // Also the implementation for window.clearInterval()
1522 if (!thisObj->inherits(&Window::info))
1523 return throwError(exec, TypeError);
1524 Window* window = static_cast<Window*>(thisObj);
1525 Frame* frame = window->impl()->frame();
1527 return jsUndefined();
1529 JSValue *v = args[0];
1531 if (!window->isSafeScript(exec))
1532 return jsUndefined();
1533 (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1534 return jsUndefined();
1537 JSValue* WindowProtoFuncSetInterval::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1539 if (!thisObj->inherits(&Window::info))
1540 return throwError(exec, TypeError);
1541 Window* window = static_cast<Window*>(thisObj);
1542 Frame* frame = window->impl()->frame();
1544 return jsUndefined();
1546 JSValue *v = args[0];
1547 UString s = v->toString(exec);
1549 if (!window->isSafeScript(exec))
1550 return jsUndefined();
1551 if (args.size() >= 2 && v->isString()) {
1552 int i = args[1]->toInt32(exec);
1553 int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1556 else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1557 JSValue *func = args[0];
1558 int i = args[1]->toInt32(exec);
1561 args.getSlice(2, argsTail);
1563 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, false);
1567 return jsUndefined();
1571 JSValue* WindowProtoFuncAddEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1573 if (!thisObj->inherits(&Window::info))
1574 return throwError(exec, TypeError);
1575 Window* window = static_cast<Window*>(thisObj);
1576 Frame* frame = window->impl()->frame();
1578 return jsUndefined();
1580 if (!window->isSafeScript(exec))
1581 return jsUndefined();
1582 if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1]))
1583 if (Document *doc = frame->document())
1584 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1585 return jsUndefined();
1588 JSValue* WindowProtoFuncRemoveEventListener::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1590 if (!thisObj->inherits(&Window::info))
1591 return throwError(exec, TypeError);
1592 Window* window = static_cast<Window*>(thisObj);
1593 Frame* frame = window->impl()->frame();
1595 return jsUndefined();
1597 if (!window->isSafeScript(exec))
1598 return jsUndefined();
1599 if (JSEventListener* listener = window->findJSEventListener(args[1]))
1600 if (Document *doc = frame->document())
1601 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1602 return jsUndefined();
1605 JSValue* WindowProtoFuncShowModalDialog::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1607 if (!thisObj->inherits(&Window::info))
1608 return throwError(exec, TypeError);
1609 Window* window = static_cast<Window*>(thisObj);
1610 Frame* frame = window->impl()->frame();
1612 return jsUndefined();
1614 return showModalDialog(exec, window, args);
1617 JSValue* WindowProtoFuncNotImplemented::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
1619 if (!thisObj->inherits(&Window::info))
1620 return throwError(exec, TypeError);
1621 Window* window = static_cast<Window*>(thisObj);
1622 Frame* frame = window->impl()->frame();
1624 return jsUndefined();
1626 // If anyone implements these, they need the safe script security check.
1627 if (!window->isSafeScript(exec))
1628 return jsUndefined();
1630 return jsUndefined();
1633 void Window::updateLayout() const
1635 Frame* frame = impl()->frame();
1638 WebCore::Document* docimpl = frame->document();
1640 docimpl->updateLayoutIgnorePendingStylesheets();
1643 void Window::setReturnValueSlot(JSValue** slot)
1645 d->m_returnValueSlot = slot;
1648 ////////////////////// ScheduledAction ////////////////////////
1650 void ScheduledAction::execute(Window* window)
1652 RefPtr<Frame> frame = window->impl()->frame();
1656 KJSProxy* scriptProxy = frame->scriptProxy();
1660 RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
1662 interpreter->setProcessingTimerCallback(true);
1664 if (JSValue* func = m_func.get()) {
1666 if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
1667 ExecState* exec = interpreter->globalExec();
1668 ASSERT(window == interpreter->globalObject());
1671 size_t size = m_args.size();
1672 for (size_t i = 0; i < size; ++i) {
1673 args.append(m_args[i]);
1676 interpreter->startTimeoutCheck();
1677 static_cast<JSObject*>(func)->call(exec, window, args);
1678 interpreter->stopTimeoutCheck();
1679 if (exec->hadException()) {
1680 JSObject* exception = exec->exception()->toObject(exec);
1681 exec->clearException();
1682 String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
1683 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1684 if (Interpreter::shouldPrintExceptions())
1685 printf("(timer):%s\n", message.utf8().data());
1686 if (Page* page = frame->page())
1687 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
1691 frame->loader()->executeScript(m_code);
1693 // Update our document's rendering following the execution of the timeout callback.
1694 // FIXME: Why not use updateDocumentsRendering to update rendering of all documents?
1695 // FIXME: Is this really the right point to do the update? We need a place that works
1696 // for all possible entry points that might possibly execute script, but this seems
1697 // to be a bit too low-level.
1698 if (Document* doc = frame->document())
1699 doc->updateRendering();
1701 interpreter->setProcessingTimerCallback(false);
1704 ////////////////////// timeouts ////////////////////////
1706 void Window::clearAllTimeouts()
1708 deleteAllValues(d->m_timeouts);
1709 d->m_timeouts.clear();
1712 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1714 int timeoutId = ++lastUsedTimeoutId;
1716 // avoid wraparound going negative on us
1720 int nestLevel = timerNestingLevel + 1;
1721 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1722 ASSERT(!d->m_timeouts.get(timeoutId));
1723 d->m_timeouts.set(timeoutId, timer);
1724 // Use a minimum interval of 10 ms to match other browsers, but only once we've
1725 // nested enough to notice that we're repeating.
1726 // Faster timers might be "better", but they're incompatible.
1727 double interval = max(0.001, t * 0.001);
1728 if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1729 interval = cMinimumTimerInterval;
1731 timer->startOneShot(interval);
1733 timer->startRepeating(interval);
1737 ScheduledAction::ScheduledAction(JSValue* func, const List& args)
1740 List::const_iterator end = args.end();
1741 for (List::const_iterator it = args.begin(); it != end; ++it)
1745 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1747 return installTimeout(new ScheduledAction(handler), t, singleShot);
1750 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1752 return installTimeout(new ScheduledAction(func, args), t, singleShot);
1755 PausedTimeouts* Window::pauseTimeouts()
1757 size_t count = d->m_timeouts.size();
1761 PausedTimeout* t = new PausedTimeout [count];
1762 PausedTimeouts* result = new PausedTimeouts(t, count);
1764 WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1765 for (size_t i = 0; i != count; ++i, ++it) {
1766 int timeoutId = it->first;
1767 DOMWindowTimer* timer = it->second;
1768 t[i].timeoutId = timeoutId;
1769 t[i].nestingLevel = timer->nestingLevel();
1770 t[i].nextFireInterval = timer->nextFireInterval();
1771 t[i].repeatInterval = timer->repeatInterval();
1772 t[i].action = timer->takeAction();
1774 ASSERT(it == d->m_timeouts.end());
1776 deleteAllValues(d->m_timeouts);
1777 d->m_timeouts.clear();
1782 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1786 size_t count = timeouts->numTimeouts();
1787 PausedTimeout* array = timeouts->takeTimeouts();
1788 for (size_t i = 0; i != count; ++i) {
1789 int timeoutId = array[i].timeoutId;
1790 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1791 d->m_timeouts.set(timeoutId, timer);
1792 timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1797 void Window::clearTimeout(int timeoutId, bool delAction)
1799 // timeout IDs have to be positive, and 0 and -1 are unsafe to
1800 // even look up since they are the empty and deleted value
1805 delete d->m_timeouts.take(timeoutId);
1808 void Window::timerFired(DOMWindowTimer* timer)
1810 // Simple case for non-one-shot timers.
1811 if (timer->isActive()) {
1812 int timeoutId = timer->timeoutId();
1814 timer->action()->execute(this);
1815 if (d->m_timeouts.contains(timeoutId) && timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1816 timer->setNestingLevel(timer->nestingLevel() + 1);
1817 if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1818 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1823 // Delete timer before executing the action for one-shot timers.
1824 ScheduledAction* action = timer->takeAction();
1825 d->m_timeouts.remove(timer->timeoutId());
1827 action->execute(this);
1833 void Window::disconnectFrame()
1837 d->loc->m_frame = 0;
1840 Window::ListenersMap& Window::jsEventListeners()
1842 return d->jsEventListeners;
1845 Window::ListenersMap& Window::jsHTMLEventListeners()
1847 return d->jsHTMLEventListeners;
1850 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1852 return d->jsUnprotectedEventListeners;
1855 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1857 return d->jsUnprotectedHTMLEventListeners;
1860 ////////////////////// Location Object ////////////////////////
1862 const ClassInfo Location::info = { "Location", 0, &LocationTable };
1864 @begin LocationTable 12
1865 assign &LocationProtoFuncAssign::create DontDelete|Function 1
1866 hash Location::Hash DontDelete
1867 host Location::Host DontDelete
1868 hostname Location::Hostname DontDelete
1869 href Location::Href DontDelete
1870 pathname Location::Pathname DontDelete
1871 port Location::Port DontDelete
1872 protocol Location::Protocol DontDelete
1873 search Location::Search DontDelete
1874 toString &LocationProtoFuncToString::create DontEnum|DontDelete|Function 0
1875 replace &LocationProtoFuncReplace::create DontDelete|Function 1
1876 reload &LocationProtoFuncReload::create DontDelete|Function 0
1880 Location::Location(Frame *p) : m_frame(p)
1884 JSValue *Location::getValueProperty(ExecState *exec, int token) const
1886 KURL url = m_frame->loader()->url();
1889 return jsString(url.ref().isNull() ? "" : "#" + url.ref());
1891 // Note: this is the IE spec. The NS spec swaps the two, it says
1892 // "The hostname property is the concatenation of the host and port properties, separated by a colon."
1894 UString str = url.host();
1896 str += ":" + String::number((int)url.port());
1897 return jsString(str);
1900 return jsString(url.host());
1903 return jsString(url.prettyURL() + "/");
1904 return jsString(url.prettyURL());
1906 return jsString(url.path().isEmpty() ? "/" : url.path());
1908 return jsString(url.port() ? String::number((int)url.port()) : "");
1910 return jsString(url.protocol() + ":");
1912 return jsString(url.query());
1915 return jsUndefined();
1919 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
1924 const Window* window = Window::retrieveWindow(m_frame);
1926 const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
1927 if (!entry || !(entry->attr & KJS::Function) || (entry->value.functionValue != &LocationProtoFuncReplace::create
1928 && entry->value.functionValue != &LocationProtoFuncReload::create
1929 && entry->value.functionValue != &LocationProtoFuncAssign::create)) {
1930 if (!window || !window->isSafeScript(exec)) {
1931 slot.setUndefined(this);
1936 return getStaticPropertySlot<Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
1939 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
1944 DeprecatedString str = v->toString(exec);
1945 KURL url = m_frame->loader()->url();
1946 const Window* window = Window::retrieveWindow(m_frame);
1947 bool sameDomainAccess = window && window->isSafeScript(exec);
1949 const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
1952 // cross-domain access to the location is allowed when assigning the whole location,
1953 // but not when assigning the individual pieces, since that might inadvertently
1954 // disclose other parts of the original location.
1955 if (entry->value.intValue != Href && !sameDomainAccess)
1958 switch (entry->value.intValue) {
1960 Frame* frame = Window::retrieveActive(exec)->impl()->frame();
1962 url = frame->loader()->completeURL(str).url();
1968 if (str.startsWith("#"))
1971 if (url.ref() == str)
1978 url.setHostAndPort(str);
1988 url.setPort(str.toUInt());
1991 url.setProtocol(str);
1997 // Disallow changing other properties in LocationTable. e.g., "window.location.toString = ...".
1998 // <http://bugs.webkit.org/show_bug.cgi?id=12720>
2002 if (sameDomainAccess)
2003 JSObject::put(exec, p, v, attr);
2007 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
2008 if (!url.url().startsWith("javascript:", false) || sameDomainAccess) {
2009 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2010 m_frame->loader()->scheduleLocationChange(url.url(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
2014 JSValue* LocationProtoFuncReplace::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2016 if (!thisObj->inherits(&Location::info))
2017 return throwError(exec, TypeError);
2018 Location* location = static_cast<Location*>(thisObj);
2019 Frame* frame = location->frame();
2021 return jsUndefined();
2023 DeprecatedString str = args[0]->toString(exec);
2024 Frame* p = Window::retrieveActive(exec)->impl()->frame();
2026 const Window* window = Window::retrieveWindow(frame);
2027 if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2028 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2029 frame->loader()->scheduleLocationChange(p->loader()->completeURL(str).url(), p->loader()->outgoingReferrer(), true, userGesture);
2033 return jsUndefined();
2036 JSValue* LocationProtoFuncReload::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2038 if (!thisObj->inherits(&Location::info))
2039 return throwError(exec, TypeError);
2040 Location* location = static_cast<Location*>(thisObj);
2041 Frame* frame = location->frame();
2043 return jsUndefined();
2045 Window* window = Window::retrieveWindow(frame);
2046 if (!window->isSafeScript(exec))
2047 return jsUndefined();
2049 if (!frame->loader()->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2050 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2051 frame->loader()->scheduleRefresh(userGesture);
2053 return jsUndefined();
2056 JSValue* LocationProtoFuncAssign::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2058 if (!thisObj->inherits(&Location::info))
2059 return throwError(exec, TypeError);
2060 Location* location = static_cast<Location*>(thisObj);
2061 Frame* frame = location->frame();
2063 return jsUndefined();
2065 Window* window = Window::retrieveWindow(frame);
2066 if (!window->isSafeScript(exec))
2067 return jsUndefined();
2069 Frame *p = Window::retrieveActive(exec)->impl()->frame();
2071 const Window *window = Window::retrieveWindow(frame);
2072 DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(args[0]->toString(exec))).url();
2073 if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
2074 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
2075 // We want a new history item if this JS was called via a user gesture
2076 frame->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
2080 return jsUndefined();
2083 JSValue* LocationProtoFuncToString::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
2085 if (!thisObj->inherits(&Location::info))
2086 return throwError(exec, TypeError);
2087 Location* location = static_cast<Location*>(thisObj);
2088 Frame* frame = location->frame();
2090 return jsUndefined();
2092 Window* window = Window::retrieveWindow(frame);
2093 if (!window->isSafeScript(exec))
2094 return jsUndefined();
2096 if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
2099 if (!frame->loader()->url().hasPath())
2100 return jsString(frame->loader()->url().prettyURL() + "/");
2101 return jsString(frame->loader()->url().prettyURL());
2104 /////////////////////////////////////////////////////////////////////////////
2106 PausedTimeouts::~PausedTimeouts()
2108 PausedTimeout *array = m_array;
2111 size_t count = m_length;
2112 for (size_t i = 0; i != count; ++i)
2113 delete array[i].action;
2117 void DOMWindowTimer::fired()
2119 timerNestingLevel = m_nestingLevel;
2120 m_object->timerFired(this);
2121 timerNestingLevel = 0;
2126 using namespace KJS;
2130 JSValue* toJS(ExecState*, DOMWindow* domWindow)
2134 Frame* frame = domWindow->frame();
2137 return Window::retrieve(frame);
2140 } // namespace WebCore