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 Window::AToB DontDelete|Function 1
156 btoa Window::BToA DontDelete|Function 1
157 scroll Window::Scroll DontDelete|Function 2
158 scrollBy Window::ScrollBy DontDelete|Function 2
159 scrollTo Window::ScrollTo DontDelete|Function 2
160 moveBy Window::MoveBy DontDelete|Function 2
161 moveTo Window::MoveTo DontDelete|Function 2
162 resizeBy Window::ResizeBy DontDelete|Function 2
163 resizeTo Window::ResizeTo DontDelete|Function 2
164 open Window::Open DontDelete|Function 3
165 setTimeout Window::SetTimeout DontDelete|Function 2
166 clearTimeout Window::ClearTimeout DontDelete|Function 1
167 setInterval Window::SetInterval DontDelete|Function 2
168 clearInterval Window::ClearInterval DontDelete|Function 1
169 captureEvents Window::CaptureEvents DontDelete|Function 0
170 releaseEvents Window::ReleaseEvents DontDelete|Function 0
171 addEventListener Window::AddEventListener DontDelete|Function 3
172 removeEventListener Window::RemoveEventListener DontDelete|Function 3
173 showModalDialog Window::ShowModalDialog DontDelete|Function 1
175 crypto Window::Crypto DontDelete|ReadOnly
176 event Window::Event_ DontDelete
177 location Window::Location_ DontDelete
178 navigator Window::Navigator_ DontDelete|ReadOnly
179 clientInformation Window::ClientInformation DontDelete|ReadOnly
180 # -- Event Listeners --
181 onabort Window::Onabort DontDelete
182 onblur Window::Onblur DontDelete
183 onchange Window::Onchange DontDelete
184 onclick Window::Onclick DontDelete
185 ondblclick Window::Ondblclick DontDelete
186 onerror Window::Onerror DontDelete
187 onfocus Window::Onfocus DontDelete
188 onkeydown Window::Onkeydown DontDelete
189 onkeypress Window::Onkeypress DontDelete
190 onkeyup Window::Onkeyup DontDelete
191 onload Window::Onload DontDelete
192 onmousedown Window::Onmousedown DontDelete
193 onmousemove Window::Onmousemove DontDelete
194 onmouseout Window::Onmouseout DontDelete
195 onmouseover Window::Onmouseover DontDelete
196 onmouseup Window::Onmouseup DontDelete
197 onmousewheel Window::OnWindowMouseWheel DontDelete
198 onreset Window::Onreset DontDelete
199 onresize Window::Onresize DontDelete
200 onscroll Window::Onscroll DontDelete
201 onsearch Window::Onsearch DontDelete
202 onselect Window::Onselect DontDelete
203 onsubmit Window::Onsubmit DontDelete
204 onunload Window::Onunload DontDelete
205 onbeforeunload Window::Onbeforeunload DontDelete
207 Audio Window::Audio DontDelete
208 DOMException Window::DOMException DontDelete
209 Image Window::Image DontDelete
210 Option Window::Option DontDelete
211 XMLHttpRequest Window::XMLHttpRequest DontDelete
212 XSLTProcessor Window::XSLTProcessor_ DontDelete
216 Window::Window(DOMWindow* window)
218 , d(new WindowPrivate)
220 // Window destruction is not thread-safe because of
221 // the non-thread-safe WebCore structures it references.
222 Collector::collectOnMainThreadOnly(this);
229 // Clear any backpointers to the window
231 ListenersMap::iterator i2 = d->jsEventListeners.begin();
232 ListenersMap::iterator e2 = d->jsEventListeners.end();
233 for (; i2 != e2; ++i2)
234 i2->second->clearWindowObj();
235 i2 = d->jsHTMLEventListeners.begin();
236 e2 = d->jsHTMLEventListeners.end();
237 for (; i2 != e2; ++i2)
238 i2->second->clearWindowObj();
240 UnprotectedListenersMap::iterator i1 = d->jsUnprotectedEventListeners.begin();
241 UnprotectedListenersMap::iterator e1 = d->jsUnprotectedEventListeners.end();
242 for (; i1 != e1; ++i1)
243 i1->second->clearWindowObj();
244 i1 = d->jsUnprotectedHTMLEventListeners.begin();
245 e1 = d->jsUnprotectedHTMLEventListeners.end();
246 for (; i1 != e1; ++i1)
247 i1->second->clearWindowObj();
250 ScriptInterpreter* Window::interpreter() const
252 Frame* frame = impl()->frame();
256 return frame->scriptProxy()->interpreter();
259 Window *Window::retrieveWindow(Frame *f)
261 JSObject *o = retrieve(f)->getObject();
263 ASSERT(o || !f->settings() || !f->settings()->isJavaScriptEnabled());
264 return static_cast<Window *>(o);
267 Window *Window::retrieveActive(ExecState *exec)
269 JSValue *imp = exec->dynamicInterpreter()->globalObject();
271 return static_cast<Window*>(imp);
274 JSValue *Window::retrieve(Frame *p)
277 if (KJSProxy *proxy = p->scriptProxy())
278 return proxy->interpreter()->globalObject(); // the Global object is the "window"
280 return jsUndefined(); // This can happen with JS disabled on the domain of that window
283 Location *Window::location() const
286 d->loc = new Location(impl()->frame());
290 // reference our special objects during garbage collection
294 if (d->loc && !d->loc->marked())
298 static bool allowPopUp(ExecState *exec, Window *window)
300 Frame* frame = window->impl()->frame();
303 if (static_cast<ScriptInterpreter*>(exec->dynamicInterpreter())->wasRunByUserGesture())
305 Settings* settings = frame->settings();
306 return settings && settings->JavaScriptCanOpenWindowsAutomatically();
309 static HashMap<String, String> parseModalDialogFeatures(ExecState *exec, JSValue *featuresArg)
311 HashMap<String, String> map;
313 Vector<String> features = valueToStringWithUndefinedOrNullCheck(exec, featuresArg).split(';');
314 Vector<String>::const_iterator end = features.end();
315 for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
317 int pos = s.find('=');
318 int colonPos = s.find(':');
319 if (pos >= 0 && colonPos >= 0)
320 continue; // ignore any strings that have both = and :
324 // null string for value means key without value
325 map.set(s.stripWhiteSpace().lower(), String());
327 String key = s.left(pos).stripWhiteSpace().lower();
328 String val = s.substring(pos + 1).stripWhiteSpace().lower();
329 int spacePos = val.find(' ');
331 val = val.left(spacePos);
339 static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false)
341 HashMap<String, String>::const_iterator it = features.find(key);
342 if (it == features.end())
344 const String& value = it->second;
345 return value.isNull() || value == "1" || value == "yes" || value == "on";
348 static float floatFeature(const HashMap<String, String> &features, const char *key, float min, float max, float defaultValue)
350 HashMap<String, String>::const_iterator it = features.find(key);
351 if (it == features.end())
353 // FIXME: Can't distinguish "0q" from string with no digits in it -- both return d == 0 and ok == false.
354 // Would be good to tell them apart somehow since string with no digits should be default value and
355 // "0q" should be minimum value.
357 double d = it->second.toDouble(&ok);
358 if ((d == 0 && !ok) || isnan(d))
360 if (d < min || max <= min)
364 return static_cast<int>(d);
367 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
368 const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
370 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
372 ResourceRequest request;
374 request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
375 FrameLoadRequest frameRequest(request, frameName);
377 // FIXME: It's much better for client API if a new window starts with a URL, here where we
378 // know what URL we are going to open. Unfortunately, this code passes the empty string
379 // for the URL, but there's a reason for that. Before loading we have to set up the opener,
380 // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
381 // do an isSafeScript call using the window we create, which can't be done before creating it.
382 // We'd have to resolve all those issues to pass the URL instead of "".
385 Frame* newFrame = openerFrame->loader()->createWindow(frameRequest, windowFeatures, created);
389 newFrame->loader()->setOpener(openerFrame);
390 newFrame->loader()->setOpenedByDOM();
392 Window* newWindow = Window::retrieveWindow(newFrame);
395 newWindow->putDirect("dialogArguments", dialogArgs);
397 if (!url.startsWith("javascript:", false) || newWindow->isSafeScript(exec)) {
398 String completedURL = url.isEmpty() ? url : activeFrame->document()->completeURL(url);
399 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
402 newFrame->loader()->changeLocation(KURL(completedURL.deprecatedString()), activeFrame->loader()->outgoingReferrer(), false, userGesture);
403 if (Document* oldDoc = openerFrame->document()) {
404 newFrame->document()->setDomainInternal(oldDoc->domain());
405 newFrame->document()->setBaseURL(oldDoc->baseURL());
407 } else if (!url.isEmpty())
408 newFrame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
414 static bool canShowModalDialog(const Window *window)
416 Frame* frame = window->impl()->frame();
420 return frame->page()->chrome()->canRunModal();
423 static bool canShowModalDialogNow(const Window *window)
425 Frame* frame = window->impl()->frame();
429 return frame->page()->chrome()->canRunModalNow();
432 static JSValue* showModalDialog(ExecState* exec, Window* openerWindow, const List& args)
434 if (!canShowModalDialogNow(openerWindow) || !allowPopUp(exec, openerWindow))
435 return jsUndefined();
437 const HashMap<String, String> features = parseModalDialogFeatures(exec, args[2]);
439 bool trusted = false;
441 WindowFeatures wargs;
443 // The following features from Microsoft's documentation are not implemented:
444 // - default font settings
445 // - width, height, left, and top specified in units other than "px"
446 // - edge (sunken or raised, default is raised)
447 // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
448 // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
449 // - unadorned: trusted && boolFeature(features, "unadorned");
450 Frame* frame = openerWindow->impl()->frame();
452 return jsUndefined();
454 FloatRect screenRect = screenAvailableRect(frame->view());
456 wargs.width = floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
457 wargs.widthSet = true;
458 wargs.height = floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
459 wargs.heightSet = true;
461 wargs.x = floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
462 wargs.xSet = wargs.x > 0;
463 wargs.y = floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
464 wargs.ySet = wargs.y > 0;
466 if (boolFeature(features, "center", true)) {
468 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
472 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
478 wargs.resizable = boolFeature(features, "resizable");
479 wargs.scrollbarsVisible = boolFeature(features, "scroll", true);
480 wargs.statusBarVisible = boolFeature(features, "status", !trusted);
481 wargs.menuBarVisible = false;
482 wargs.toolBarVisible = false;
483 wargs.locationBarVisible = false;
484 wargs.fullscreen = false;
486 Frame* dialogFrame = createWindow(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args[0]), "", wargs, args[1]);
488 return jsUndefined();
490 Window* dialogWindow = Window::retrieveWindow(dialogFrame);
492 // Get the return value either just before clearing the dialog window's
493 // properties (in Window::clear), or when on return from runModal.
494 JSValue* returnValue = 0;
495 dialogWindow->setReturnValueSlot(&returnValue);
496 dialogFrame->page()->chrome()->runModal();
497 dialogWindow->setReturnValueSlot(0);
499 // If we don't have a return value, get it now.
500 // Either Window::clear was not called yet, or there was no return value,
501 // and in that case, there's no harm in trying again (no benefit either).
503 returnValue = dialogWindow->getDirect("returnValue");
505 return returnValue ? returnValue : jsUndefined();
508 JSValue *Window::getValueProperty(ExecState *exec, int token) const
510 ASSERT(impl()->frame());
514 if (!isSafeScript(exec))
515 return jsUndefined();
516 return jsUndefined(); // FIXME: implement this
518 if (!isSafeScript(exec))
519 return jsUndefined();
520 return getDOMExceptionConstructor(exec);
522 if (!isSafeScript(exec))
523 return jsUndefined();
525 return jsUndefined();
526 return toJS(exec, d->m_evt);
530 case ClientInformation: {
531 if (!isSafeScript(exec))
532 return jsUndefined();
533 // Store the navigator in the object so we get the same one each time.
534 Navigator *n = new Navigator(exec, impl()->frame());
535 // FIXME: this will make the "navigator" object accessible from windows that fail
536 // the security check the first time, but not subsequent times, seems weird.
537 const_cast<Window *>(this)->putDirect("navigator", n, DontDelete|ReadOnly);
538 const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete|ReadOnly);
542 if (!isSafeScript(exec))
543 return jsUndefined();
544 // FIXME: this property (and the few below) probably shouldn't create a new object every
546 return new ImageConstructorImp(exec, impl()->frame()->document());
548 if (!isSafeScript(exec))
549 return jsUndefined();
550 return new JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
552 if (!isSafeScript(exec))
553 return jsUndefined();
554 return new JSXMLHttpRequestConstructorImp(exec, impl()->frame()->document());
557 return new JSHTMLAudioElementConstructor(exec, impl()->frame()->document());
559 return jsUndefined();
563 if (!isSafeScript(exec))
564 return jsUndefined();
565 return new XSLTProcessorConstructorImp(exec);
568 return jsUndefined();
572 if (!isSafeScript(exec))
573 return jsUndefined();
577 return getListener(exec, abortEvent);
579 return getListener(exec, blurEvent);
581 return getListener(exec, changeEvent);
583 return getListener(exec, clickEvent);
585 return getListener(exec, dblclickEvent);
587 return getListener(exec, errorEvent);
589 return getListener(exec, focusEvent);
591 return getListener(exec, keydownEvent);
593 return getListener(exec, keypressEvent);
595 return getListener(exec, keyupEvent);
597 return getListener(exec, loadEvent);
599 return getListener(exec, mousedownEvent);
601 return getListener(exec, mousemoveEvent);
603 return getListener(exec, mouseoutEvent);
605 return getListener(exec, mouseoverEvent);
607 return getListener(exec, mouseupEvent);
608 case OnWindowMouseWheel:
609 return getListener(exec, mousewheelEvent);
611 return getListener(exec, resetEvent);
613 return getListener(exec,resizeEvent);
615 return getListener(exec,scrollEvent);
617 return getListener(exec,searchEvent);
619 return getListener(exec,selectEvent);
621 return getListener(exec,submitEvent);
623 return getListener(exec, beforeunloadEvent);
625 return getListener(exec, unloadEvent);
628 return jsUndefined();
631 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
633 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName)));
636 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
638 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index()));
641 JSValue *Window::namedItemGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
643 Window *thisObj = static_cast<Window *>(slot.slotBase());
644 Document *doc = thisObj->impl()->frame()->document();
645 ASSERT(thisObj->isSafeScript(exec) && doc && doc->isHTMLDocument());
647 String name = propertyName;
648 RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(name);
649 if (collection->length() == 1)
650 return toJS(exec, collection->firstItem());
651 return toJS(exec, collection.get());
654 bool Window::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
656 // Check for child frames by name before built-in properties to
657 // match Mozilla. This does not match IE, but some sites end up
658 // naming frames things that conflict with window properties that
659 // are in Moz but not IE. Since we have some of these, we have to do
661 if (impl()->frame()->tree()->child(propertyName)) {
662 slot.setCustom(this, childFrameGetter);
666 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
668 if (entry->attr & Function) {
669 switch (entry->value) {
670 case ShowModalDialog:
671 if (!canShowModalDialog(this))
675 if (isSafeScript(exec))
676 slot.setStaticEntry(this, entry, staticFunctionGetter<WindowFunc>);
678 slot.setUndefined(this);
681 slot.setStaticEntry(this, entry, staticValueGetter<Window>);
685 // FIXME: Search the whole frame hierachy somewhere around here.
686 // We need to test the correct priority order.
688 // allow window[1] or parent[1] etc. (#56983)
690 unsigned i = propertyName.toArrayIndex(&ok);
691 if (ok && i < impl()->frame()->tree()->childCount()) {
692 slot.setCustomIndex(this, i, indexGetter);
696 // allow shortcuts like 'Image1' instead of document.images.Image1
697 Document* doc = impl()->frame()->document();
698 if (doc && doc->isHTMLDocument()) {
699 if (!isSafeScript(exec)) {
700 slot.setUndefined(this);
704 AtomicString atomicPropertyName = propertyName;
705 if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
706 slot.setCustom(this, namedItemGetter);
711 if (!isSafeScript(exec)) {
712 slot.setUndefined(this);
716 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
719 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
721 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
723 if (entry->attr & Function) {
724 if (isSafeScript(exec))
725 JSObject::put(exec, propertyName, value, attr);
728 if (entry->attr & ReadOnly)
731 switch (entry->value) {
733 Frame* p = Window::retrieveActive(exec)->impl()->frame();
735 DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(value->toString(exec))).url();
736 if (!dstUrl.startsWith("javascript:", false) || isSafeScript(exec)) {
737 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
738 // We want a new history item if this JS was called via a user gesture
739 impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
745 if (isSafeScript(exec))
746 setListener(exec, abortEvent,value);
749 if (isSafeScript(exec))
750 setListener(exec, blurEvent,value);
753 if (isSafeScript(exec))
754 setListener(exec, changeEvent,value);
757 if (isSafeScript(exec))
758 setListener(exec,clickEvent,value);
761 if (isSafeScript(exec))
762 setListener(exec, dblclickEvent,value);
765 if (isSafeScript(exec))
766 setListener(exec, errorEvent, value);
769 if (isSafeScript(exec))
770 setListener(exec,focusEvent,value);
773 if (isSafeScript(exec))
774 setListener(exec,keydownEvent,value);
777 if (isSafeScript(exec))
778 setListener(exec,keypressEvent,value);
781 if (isSafeScript(exec))
782 setListener(exec,keyupEvent,value);
785 if (isSafeScript(exec))
786 setListener(exec,loadEvent,value);
789 if (isSafeScript(exec))
790 setListener(exec,mousedownEvent,value);
793 if (isSafeScript(exec))
794 setListener(exec,mousemoveEvent,value);
797 if (isSafeScript(exec))
798 setListener(exec,mouseoutEvent,value);
801 if (isSafeScript(exec))
802 setListener(exec,mouseoverEvent,value);
805 if (isSafeScript(exec))
806 setListener(exec,mouseupEvent,value);
808 case OnWindowMouseWheel:
809 if (isSafeScript(exec))
810 setListener(exec, mousewheelEvent,value);
813 if (isSafeScript(exec))
814 setListener(exec,resetEvent,value);
817 if (isSafeScript(exec))
818 setListener(exec,resizeEvent,value);
821 if (isSafeScript(exec))
822 setListener(exec,scrollEvent,value);
825 if (isSafeScript(exec))
826 setListener(exec,searchEvent,value);
829 if (isSafeScript(exec))
830 setListener(exec,selectEvent,value);
833 if (isSafeScript(exec))
834 setListener(exec,submitEvent,value);
837 if (isSafeScript(exec))
838 setListener(exec, beforeunloadEvent, value);
841 if (isSafeScript(exec))
842 setListener(exec, unloadEvent, value);
848 if (isSafeScript(exec))
849 JSObject::put(exec, propertyName, value, attr);
852 static bool shouldLoadAsEmptyDocument(const KURL &url)
854 return url.protocol().lower() == "about" || url.isEmpty();
857 bool Window::isSafeScript(const ScriptInterpreter *origin, const ScriptInterpreter *target)
859 if (origin == target)
862 Frame* originFrame = origin->frame();
863 Frame* targetFrame = target->frame();
865 // JS may be attempting to access the "window" object, which should be valid,
866 // even if the document hasn't been constructed yet. If the document doesn't
867 // exist yet allow JS to access the window object.
868 if (!targetFrame->document())
871 WebCore::Document *originDocument = originFrame->document();
872 WebCore::Document *targetDocument = targetFrame->document();
874 if (!targetDocument) {
878 WebCore::String targetDomain = targetDocument->domain();
880 // Always allow local pages to execute any JS.
881 if (targetDomain.isNull())
884 WebCore::String originDomain = originDocument->domain();
886 // if this document is being initially loaded as empty by its parent
887 // or opener, allow access from any document in the same domain as
888 // the parent or opener.
889 if (shouldLoadAsEmptyDocument(targetFrame->loader()->url())) {
890 Frame* ancestorFrame = targetFrame->loader()->opener() ? targetFrame->loader()->opener() : targetFrame->tree()->parent();
891 while (ancestorFrame && shouldLoadAsEmptyDocument(ancestorFrame->loader()->url()))
892 ancestorFrame = ancestorFrame->tree()->parent();
894 originDomain = ancestorFrame->document()->domain();
897 if ( targetDomain == originDomain )
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());
904 String message = String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains must match.\n",
905 targetDocument->URL().latin1(), originDocument->URL().latin1());
906 if (Page* page = targetFrame->page())
907 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 *WindowFunc::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 FrameView *widget = frame->view();
1246 Page* page = frame->page();
1247 JSValue *v = args[0];
1248 UString s = v->toString(exec);
1254 case Window::BToA: {
1255 if (args.size() < 1)
1256 return throwError(exec, SyntaxError, "Not enough arguments");
1260 setDOMException(exec, INVALID_CHARACTER_ERR);
1261 return jsUndefined();
1264 Vector<char> in(s.size());
1265 for (int i = 0; i < s.size(); ++i)
1266 in[i] = static_cast<char>(s.data()[i].unicode());
1269 if (id == Window::AToB) {
1270 if (!base64Decode(in, out))
1271 return throwError(exec, GeneralError, "Cannot decode base64");
1273 base64Encode(in, out);
1275 return jsString(String(out.data(), out.size()));
1279 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1280 AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1282 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1283 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1284 if (!allowPopUp(exec, window) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1285 return jsUndefined();
1287 // Get the target frame for the special cases of _top and _parent
1288 if (frameName == "_top")
1289 while (frame->tree()->parent())
1290 frame = frame->tree()->parent();
1291 else if (frameName == "_parent")
1292 if (frame->tree()->parent())
1293 frame = frame->tree()->parent();
1295 // In those cases, we can schedule a location change right now and return early
1296 if (frameName == "_top" || frameName == "_parent") {
1297 String completedURL;
1298 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1299 if (!urlString.isEmpty() && activeFrame)
1300 completedURL = activeFrame->document()->completeURL(urlString);
1302 const Window* window = Window::retrieveWindow(frame);
1303 if (!completedURL.isEmpty() && (!completedURL.startsWith("javascript:", false) || (window && window->isSafeScript(exec)))) {
1304 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1305 frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1307 return Window::retrieve(frame);
1310 // In the case of a named frame or a new window, we'll use the createWindow() helper
1311 WindowFeatures windowFeatures;
1312 String features = valueToStringWithUndefinedOrNullCheck(exec, args[2]);
1313 parseWindowFeatures(features, windowFeatures);
1314 FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1315 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1317 windowFeatures.x = windowRect.x();
1318 windowFeatures.y = windowRect.y();
1319 windowFeatures.height = windowRect.height();
1320 windowFeatures.width = windowRect.width();
1322 frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1325 return jsUndefined();
1327 return Window::retrieve(frame); // global object
1329 case Window::ScrollBy:
1330 window->updateLayout();
1331 if(args.size() >= 2 && widget)
1332 widget->scrollBy(args[0]->toInt32(exec), args[1]->toInt32(exec));
1333 return jsUndefined();
1334 case Window::Scroll:
1335 case Window::ScrollTo:
1336 window->updateLayout();
1337 if (args.size() >= 2 && widget)
1338 widget->setContentsPos(args[0]->toInt32(exec), args[1]->toInt32(exec));
1339 return jsUndefined();
1340 case Window::MoveBy:
1341 if (args.size() >= 2 && page) {
1342 FloatRect fr = page->chrome()->windowRect();
1343 FloatRect update = fr;
1344 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1345 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1346 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1347 page->chrome()->setWindowRect(fr);
1349 return jsUndefined();
1350 case Window::MoveTo:
1351 if (args.size() >= 2 && page) {
1352 FloatRect fr = page->chrome()->windowRect();
1353 FloatRect sr = screenAvailableRect(page->mainFrame()->view());
1354 fr.setLocation(sr.location());
1355 FloatRect update = fr;
1356 update.move(args[0]->toFloat(exec), args[1]->toFloat(exec));
1357 // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
1358 adjustWindowRect(sr, fr, update);
1359 page->chrome()->setWindowRect(fr);
1361 return jsUndefined();
1362 case Window::ResizeBy:
1363 if (args.size() >= 2 && page) {
1364 FloatRect fr = page->chrome()->windowRect();
1365 FloatSize dest = fr.size() + FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1366 FloatRect update(fr.location(), dest);
1367 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1368 page->chrome()->setWindowRect(fr);
1370 return jsUndefined();
1371 case Window::ResizeTo:
1372 if (args.size() >= 2 && page) {
1373 FloatRect fr = page->chrome()->windowRect();
1374 FloatSize dest = FloatSize(args[0]->toFloat(exec), args[1]->toFloat(exec));
1375 FloatRect update(fr.location(), dest);
1376 adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
1377 page->chrome()->setWindowRect(fr);
1379 return jsUndefined();
1380 case Window::SetTimeout:
1381 if (!window->isSafeScript(exec))
1382 return jsUndefined();
1383 if (v->isString()) {
1384 int i = args[1]->toInt32(exec);
1385 int r = (const_cast<Window*>(window))->installTimeout(s, i, true /*single shot*/);
1388 else if (v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1389 JSValue *func = args[0];
1390 int i = args[1]->toInt32(exec);
1393 args.getSlice(2, argsTail);
1395 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, true /*single shot*/);
1399 return jsUndefined();
1400 case Window::SetInterval:
1401 if (!window->isSafeScript(exec))
1402 return jsUndefined();
1403 if (args.size() >= 2 && v->isString()) {
1404 int i = args[1]->toInt32(exec);
1405 int r = (const_cast<Window*>(window))->installTimeout(s, i, false);
1408 else if (args.size() >= 2 && v->isObject() && static_cast<JSObject *>(v)->implementsCall()) {
1409 JSValue *func = args[0];
1410 int i = args[1]->toInt32(exec);
1413 args.getSlice(2, argsTail);
1415 int r = (const_cast<Window*>(window))->installTimeout(func, argsTail, i, false);
1419 return jsUndefined();
1420 case Window::ClearTimeout:
1421 case Window::ClearInterval:
1422 if (!window->isSafeScript(exec))
1423 return jsUndefined();
1424 (const_cast<Window*>(window))->clearTimeout(v->toInt32(exec));
1425 return jsUndefined();
1426 case Window::CaptureEvents:
1427 case Window::ReleaseEvents:
1428 // If anyone implements these, they need the safe script security check.
1429 if (!window->isSafeScript(exec))
1430 return jsUndefined();
1432 return jsUndefined();
1433 case Window::AddEventListener:
1434 if (!window->isSafeScript(exec))
1435 return jsUndefined();
1436 if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1]))
1437 if (Document *doc = frame->document())
1438 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1439 return jsUndefined();
1440 case Window::RemoveEventListener:
1441 if (!window->isSafeScript(exec))
1442 return jsUndefined();
1443 if (JSEventListener* listener = window->findJSEventListener(args[1]))
1444 if (Document *doc = frame->document())
1445 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1446 return jsUndefined();
1447 case Window::ShowModalDialog: {
1448 JSValue* result = showModalDialog(exec, window, args);
1452 return jsUndefined();
1455 void Window::updateLayout() const
1457 Frame* frame = impl()->frame();
1460 WebCore::Document* docimpl = frame->document();
1462 docimpl->updateLayoutIgnorePendingStylesheets();
1465 void Window::setReturnValueSlot(JSValue** slot)
1467 d->m_returnValueSlot = slot;
1470 ////////////////////// ScheduledAction ////////////////////////
1472 void ScheduledAction::execute(Window* window)
1474 RefPtr<Frame> frame = window->impl()->frame();
1478 KJSProxy* scriptProxy = frame->scriptProxy();
1482 RefPtr<ScriptInterpreter> interpreter = scriptProxy->interpreter();
1484 interpreter->setProcessingTimerCallback(true);
1486 if (JSValue* func = m_func.get()) {
1488 if (func->isObject() && static_cast<JSObject*>(func)->implementsCall()) {
1489 ExecState* exec = interpreter->globalExec();
1490 ASSERT(window == interpreter->globalObject());
1493 size_t size = m_args.size();
1494 for (size_t i = 0; i < size; ++i) {
1495 args.append(m_args[i]);
1498 interpreter->startTimeoutCheck();
1499 static_cast<JSObject*>(func)->call(exec, window, args);
1500 interpreter->stopTimeoutCheck();
1501 if (exec->hadException()) {
1502 JSObject* exception = exec->exception()->toObject(exec);
1503 exec->clearException();
1504 String message = exception->get(exec, exec->propertyNames().message)->toString(exec);
1505 int lineNumber = exception->get(exec, "line")->toInt32(exec);
1506 if (Interpreter::shouldPrintExceptions())
1507 printf("(timer):%s\n", message.utf8().data());
1508 if (Page* page = frame->page())
1509 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, lineNumber, String());
1513 frame->loader()->executeScript(m_code);
1515 // Update our document's rendering following the execution of the timeout callback.
1516 // FIXME: Why not use updateDocumentsRendering to update rendering of all documents?
1517 // FIXME: Is this really the right point to do the update? We need a place that works
1518 // for all possible entry points that might possibly execute script, but this seems
1519 // to be a bit too low-level.
1520 if (Document* doc = frame->document())
1521 doc->updateRendering();
1523 interpreter->setProcessingTimerCallback(false);
1526 ////////////////////// timeouts ////////////////////////
1528 void Window::clearAllTimeouts()
1530 deleteAllValues(d->m_timeouts);
1531 d->m_timeouts.clear();
1534 int Window::installTimeout(ScheduledAction* a, int t, bool singleShot)
1536 int timeoutId = ++lastUsedTimeoutId;
1538 // avoid wraparound going negative on us
1542 int nestLevel = timerNestingLevel + 1;
1543 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1544 ASSERT(!d->m_timeouts.get(timeoutId));
1545 d->m_timeouts.set(timeoutId, timer);
1546 // Use a minimum interval of 10 ms to match other browsers, but only once we've
1547 // nested enough to notice that we're repeating.
1548 // Faster timers might be "better", but they're incompatible.
1549 double interval = max(0.001, t * 0.001);
1550 if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1551 interval = cMinimumTimerInterval;
1553 timer->startOneShot(interval);
1555 timer->startRepeating(interval);
1559 ScheduledAction::ScheduledAction(JSValue* func, const List& args)
1562 List::const_iterator end = args.end();
1563 for (List::const_iterator it = args.begin(); it != end; ++it)
1567 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1569 return installTimeout(new ScheduledAction(handler), t, singleShot);
1572 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1574 return installTimeout(new ScheduledAction(func, args), t, singleShot);
1577 PausedTimeouts* Window::pauseTimeouts()
1579 size_t count = d->m_timeouts.size();
1583 PausedTimeout* t = new PausedTimeout [count];
1584 PausedTimeouts* result = new PausedTimeouts(t, count);
1586 WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1587 for (size_t i = 0; i != count; ++i, ++it) {
1588 int timeoutId = it->first;
1589 DOMWindowTimer* timer = it->second;
1590 t[i].timeoutId = timeoutId;
1591 t[i].nestingLevel = timer->nestingLevel();
1592 t[i].nextFireInterval = timer->nextFireInterval();
1593 t[i].repeatInterval = timer->repeatInterval();
1594 t[i].action = timer->takeAction();
1596 ASSERT(it == d->m_timeouts.end());
1598 deleteAllValues(d->m_timeouts);
1599 d->m_timeouts.clear();
1604 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1608 size_t count = timeouts->numTimeouts();
1609 PausedTimeout* array = timeouts->takeTimeouts();
1610 for (size_t i = 0; i != count; ++i) {
1611 int timeoutId = array[i].timeoutId;
1612 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1613 d->m_timeouts.set(timeoutId, timer);
1614 timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1619 void Window::clearTimeout(int timeoutId, bool delAction)
1621 // timeout IDs have to be positive, and 0 and -1 are unsafe to
1622 // even look up since they are the empty and deleted value
1627 delete d->m_timeouts.take(timeoutId);
1630 void Window::timerFired(DOMWindowTimer* timer)
1632 // Simple case for non-one-shot timers.
1633 if (timer->isActive()) {
1634 int timeoutId = timer->timeoutId();
1636 timer->action()->execute(this);
1637 if (d->m_timeouts.contains(timeoutId) && timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1638 timer->setNestingLevel(timer->nestingLevel() + 1);
1639 if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1640 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1645 // Delete timer before executing the action for one-shot timers.
1646 ScheduledAction* action = timer->takeAction();
1647 d->m_timeouts.remove(timer->timeoutId());
1649 action->execute(this);
1655 void Window::disconnectFrame()
1659 d->loc->m_frame = 0;
1662 Window::ListenersMap& Window::jsEventListeners()
1664 return d->jsEventListeners;
1667 Window::ListenersMap& Window::jsHTMLEventListeners()
1669 return d->jsHTMLEventListeners;
1672 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1674 return d->jsUnprotectedEventListeners;
1677 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1679 return d->jsUnprotectedHTMLEventListeners;
1682 ////////////////////// Location Object ////////////////////////
1684 const ClassInfo Location::info = { "Location", 0, &LocationTable };
1686 @begin LocationTable 12
1687 assign Location::Assign DontDelete|Function 1
1688 hash Location::Hash DontDelete
1689 host Location::Host DontDelete
1690 hostname Location::Hostname DontDelete
1691 href Location::Href DontDelete
1692 pathname Location::Pathname DontDelete
1693 port Location::Port DontDelete
1694 protocol Location::Protocol DontDelete
1695 search Location::Search DontDelete
1696 toString Location::ToString DontEnum|DontDelete|Function 0
1697 replace Location::Replace DontDelete|Function 1
1698 reload Location::Reload DontDelete|Function 0
1701 KJS_IMPLEMENT_PROTOTYPE_FUNCTION(LocationFunc)
1702 Location::Location(Frame *p) : m_frame(p)
1706 JSValue *Location::getValueProperty(ExecState *exec, int token) const
1708 KURL url = m_frame->loader()->url();
1711 return jsString(url.ref().isNull() ? "" : "#" + url.ref());
1713 // Note: this is the IE spec. The NS spec swaps the two, it says
1714 // "The hostname property is the concatenation of the host and port properties, separated by a colon."
1716 UString str = url.host();
1718 str += ":" + String::number((int)url.port());
1719 return jsString(str);
1722 return jsString(url.host());
1725 return jsString(url.prettyURL() + "/");
1726 return jsString(url.prettyURL());
1728 return jsString(url.path().isEmpty() ? "/" : url.path());
1730 return jsString(url.port() ? String::number((int)url.port()) : "");
1732 return jsString(url.protocol() + ":");
1734 return jsString(url.query());
1737 return jsUndefined();
1741 bool Location::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
1746 const Window* window = Window::retrieveWindow(m_frame);
1748 const HashEntry *entry = Lookup::findEntry(&LocationTable, propertyName);
1749 if (!entry || (entry->value != Replace && entry->value != Reload && entry->value != Assign))
1750 if (!window || !window->isSafeScript(exec)) {
1751 slot.setUndefined(this);
1755 return getStaticPropertySlot<LocationFunc, Location, JSObject>(exec, &LocationTable, this, propertyName, slot);
1758 void Location::put(ExecState *exec, const Identifier &p, JSValue *v, int attr)
1763 DeprecatedString str = v->toString(exec);
1764 KURL url = m_frame->loader()->url();
1765 const Window* window = Window::retrieveWindow(m_frame);
1766 bool sameDomainAccess = window && window->isSafeScript(exec);
1768 const HashEntry *entry = Lookup::findEntry(&LocationTable, p);
1771 // cross-domain access to the location is allowed when assigning the whole location,
1772 // but not when assigning the individual pieces, since that might inadvertently
1773 // disclose other parts of the original location.
1774 if (entry->value != Href && !sameDomainAccess)
1777 switch (entry->value) {
1779 Frame* frame = Window::retrieveActive(exec)->impl()->frame();
1781 url = frame->loader()->completeURL(str).url();
1787 if (str.startsWith("#"))
1790 if (url.ref() == str)
1797 url.setHostAndPort(str);
1807 url.setPort(str.toUInt());
1810 url.setProtocol(str);
1816 // Disallow changing other properties in LocationTable. e.g., "window.location.toString = ...".
1817 // <http://bugs.webkit.org/show_bug.cgi?id=12720>
1821 if (sameDomainAccess)
1822 JSObject::put(exec, p, v, attr);
1826 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1827 if (!url.url().startsWith("javascript:", false) || sameDomainAccess) {
1828 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1829 m_frame->loader()->scheduleLocationChange(url.url(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
1833 JSValue *LocationFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
1835 if (!thisObj->inherits(&Location::info))
1836 return throwError(exec, TypeError);
1837 Location *location = static_cast<Location *>(thisObj);
1838 Frame *frame = location->frame();
1841 Window* window = Window::retrieveWindow(frame);
1842 if (id != Location::Replace && !window->isSafeScript(exec))
1843 return jsUndefined();
1846 case Location::Replace:
1848 DeprecatedString str = args[0]->toString(exec);
1849 Frame* p = Window::retrieveActive(exec)->impl()->frame();
1851 const Window* window = Window::retrieveWindow(frame);
1852 if (!str.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1853 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1854 frame->loader()->scheduleLocationChange(p->loader()->completeURL(str).url(), p->loader()->outgoingReferrer(), true, userGesture);
1859 case Location::Reload:
1861 const Window* window = Window::retrieveWindow(frame);
1862 if (!frame->loader()->url().url().startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1863 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1864 frame->loader()->scheduleRefresh(userGesture);
1868 case Location::Assign:
1870 Frame *p = Window::retrieveActive(exec)->impl()->frame();
1872 const Window *window = Window::retrieveWindow(frame);
1873 DeprecatedString dstUrl = p->loader()->completeURL(DeprecatedString(args[0]->toString(exec))).url();
1874 if (!dstUrl.startsWith("javascript:", false) || (window && window->isSafeScript(exec))) {
1875 bool userGesture = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter())->wasRunByUserGesture();
1876 // We want a new history item if this JS was called via a user gesture
1877 frame->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
1882 case Location::ToString:
1883 if (!frame || !Window::retrieveWindow(frame)->isSafeScript(exec))
1886 if (!frame->loader()->url().hasPath())
1887 return jsString(frame->loader()->url().prettyURL() + "/");
1888 return jsString(frame->loader()->url().prettyURL());
1891 return jsUndefined();
1894 /////////////////////////////////////////////////////////////////////////////
1896 PausedTimeouts::~PausedTimeouts()
1898 PausedTimeout *array = m_array;
1901 size_t count = m_length;
1902 for (size_t i = 0; i != count; ++i)
1903 delete array[i].action;
1907 void DOMWindowTimer::fired()
1909 timerNestingLevel = m_nestingLevel;
1910 m_object->timerFired(this);
1911 timerNestingLevel = 0;
1916 using namespace KJS;
1920 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1924 Frame* frame = domWindow->frame();
1927 return Window::retrieve(frame);
1930 } // namespace WebCore