2 * Copyright (C) 2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reseved.
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
24 #include "kjs_window.h"
29 #include "DOMWindow.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "ExceptionCode.h"
34 #include "FloatRect.h"
36 #include "FrameLoadRequest.h"
37 #include "FrameLoader.h"
38 #include "FrameTree.h"
39 #include "GCController.h"
40 #include "HTMLDocument.h"
41 #include "JSDOMWindow.h"
43 #include "JSAudioConstructor.h"
44 #include "JSHTMLCollection.h"
45 #include "JSHTMLOptionElementConstructor.h"
46 #include "JSXMLHttpRequest.h"
47 #include "JSLocation.h"
49 #include "MediaPlayer.h"
51 #include "PausedTimeouts.h"
52 #include "PlatformScreen.h"
53 #include "PluginInfoStore.h"
54 #include "RenderView.h"
55 #include "ScheduledAction.h"
56 #include "SecurityOrigin.h"
58 #include "WindowFeatures.h"
59 #include "htmlediting.h"
61 #include "kjs_events.h"
62 #include "kjs_navigator.h"
63 #include "kjs_proxy.h"
64 #include <wtf/AlwaysInline.h>
65 #include <wtf/MathExtras.h>
68 #include "JSXSLTProcessor.h"
71 using namespace WebCore;
72 using namespace EventNames;
76 static int lastUsedTimeoutId;
78 static int timerNestingLevel = 0;
79 const int cMaxTimerNestingLevel = 5;
80 const double cMinimumTimerInterval = 0.010;
82 struct WindowPrivate {
86 , m_returnValueSlot(0)
90 Window::ListenersMap jsEventListeners;
91 Window::ListenersMap jsHTMLEventListeners;
92 Window::UnprotectedListenersMap jsUnprotectedEventListeners;
93 Window::UnprotectedListenersMap jsUnprotectedHTMLEventListeners;
94 mutable WebCore::JSLocation* loc;
95 WebCore::Event* m_evt;
96 JSValue** m_returnValueSlot;
98 typedef HashMap<int, DOMWindowTimer*> TimeoutsMap;
99 TimeoutsMap m_timeouts;
102 class DOMWindowTimer : public TimerBase {
104 DOMWindowTimer(int timeoutId, int nestingLevel, Window* object, WebCore::ScheduledAction* action)
105 : m_timeoutId(timeoutId)
106 , m_nestingLevel(nestingLevel)
112 virtual ~DOMWindowTimer()
118 int timeoutId() const { return m_timeoutId; }
120 int nestingLevel() const { return m_nestingLevel; }
121 void setNestingLevel(int n) { m_nestingLevel = n; }
123 WebCore::ScheduledAction* action() const { return m_action; }
124 WebCore::ScheduledAction* takeAction() { WebCore::ScheduledAction* a = m_action; m_action = 0; return a; }
127 virtual void fired();
132 WebCore::ScheduledAction* m_action;
137 #include "kjs_window.lut.h"
141 ////////////////////// Window Object ////////////////////////
143 const ClassInfo Window::info = { "Window", 0, &WindowTable };
146 @begin WindowTable 118
147 # Warning, when adding a function to this object you need to add a case in Window::get
149 atob windowProtoFuncAToB DontDelete|Function 1
150 btoa windowProtoFuncBToA DontDelete|Function 1
151 open windowProtoFuncOpen DontDelete|Function 3
152 setTimeout windowProtoFuncSetTimeout DontDelete|Function 2
153 clearTimeout windowProtoFuncClearTimeout DontDelete|Function 1
154 setInterval windowProtoFuncSetInterval DontDelete|Function 2
155 clearInterval windowProtoFuncClearTimeout DontDelete|Function 1
156 addEventListener windowProtoFuncAddEventListener DontDelete|Function 3
157 removeEventListener windowProtoFuncRemoveEventListener DontDelete|Function 3
158 showModalDialog windowProtoFuncShowModalDialog DontDelete|Function 1
160 captureEvents windowProtoFuncNotImplemented DontDelete|Function 0
161 releaseEvents windowProtoFuncNotImplemented DontDelete|Function 0
164 crypto Window::Crypto DontDelete|ReadOnly
165 event Window::Event_ DontDelete
166 location Window::Location_ DontDelete
167 navigator Window::Navigator_ DontDelete
168 clientInformation Window::ClientInformation DontDelete
169 # -- Event Listeners --
170 onabort Window::Onabort DontDelete
171 onblur Window::Onblur DontDelete
172 onchange Window::Onchange DontDelete
173 onclick Window::Onclick DontDelete
174 ondblclick Window::Ondblclick DontDelete
175 onerror Window::Onerror DontDelete
176 onfocus Window::Onfocus DontDelete
177 onkeydown Window::Onkeydown DontDelete
178 onkeypress Window::Onkeypress DontDelete
179 onkeyup Window::Onkeyup DontDelete
180 onload Window::Onload DontDelete
181 onmousedown Window::Onmousedown DontDelete
182 onmousemove Window::Onmousemove DontDelete
183 onmouseout Window::Onmouseout DontDelete
184 onmouseover Window::Onmouseover DontDelete
185 onmouseup Window::Onmouseup DontDelete
186 onmousewheel Window::OnWindowMouseWheel DontDelete
187 onreset Window::Onreset DontDelete
188 onresize Window::Onresize DontDelete
189 onscroll Window::Onscroll DontDelete
190 onsearch Window::Onsearch DontDelete
191 onselect Window::Onselect DontDelete
192 onsubmit Window::Onsubmit DontDelete
193 onunload Window::Onunload DontDelete
194 onbeforeunload Window::Onbeforeunload DontDelete
196 Audio Window::Audio DontDelete
197 Image Window::Image DontDelete
198 Option Window::Option DontDelete
199 XMLHttpRequest Window::XMLHttpRequest DontDelete
200 XSLTProcessor Window::XSLTProcessor_ DontDelete
204 Window::Window(JSObject* prototype, DOMWindow* window)
205 : JSGlobalObject(prototype)
207 , d(new WindowPrivate)
209 // Window destruction is not thread-safe because of
210 // the non-thread-safe WebCore structures it references.
211 Collector::collectOnMainThreadOnly(this);
213 // Time in milliseconds before the script timeout handler kicks in.
214 setTimeoutTime(10000);
221 // Clear any backpointers to the window
223 ListenersMap::iterator i2 = d->jsEventListeners.begin();
224 ListenersMap::iterator e2 = d->jsEventListeners.end();
225 for (; i2 != e2; ++i2)
226 i2->second->clearWindowObj();
227 i2 = d->jsHTMLEventListeners.begin();
228 e2 = d->jsHTMLEventListeners.end();
229 for (; i2 != e2; ++i2)
230 i2->second->clearWindowObj();
232 UnprotectedListenersMap::iterator i1 = d->jsUnprotectedEventListeners.begin();
233 UnprotectedListenersMap::iterator e1 = d->jsUnprotectedEventListeners.end();
234 for (; i1 != e1; ++i1)
235 i1->second->clearWindowObj();
236 i1 = d->jsUnprotectedHTMLEventListeners.begin();
237 e1 = d->jsUnprotectedHTMLEventListeners.end();
238 for (; i1 != e1; ++i1)
239 i1->second->clearWindowObj();
242 Window* Window::retrieveWindow(Frame* frame)
244 JSObject* o = retrieve(frame)->getObject();
246 ASSERT(o || !frame->scriptProxy()->isEnabled());
247 return static_cast<Window*>(o);
250 Window* Window::retrieveActive(ExecState* exec)
252 JSGlobalObject* globalObject = exec->dynamicGlobalObject();
253 ASSERT(globalObject);
254 return static_cast<Window*>(globalObject);
257 JSValue* Window::retrieve(Frame* frame)
260 if (frame->scriptProxy()->isEnabled())
261 return frame->scriptProxy()->globalObject(); // the Global object is the "window"
263 return jsUndefined(); // This can happen with JS disabled on the domain of that window
266 WebCore::JSLocation* Window::location() const
269 d->loc = new JSLocation(0, impl()->frame()); // FIXME: we need to pass a prototype.
276 if (d->loc && !d->loc->marked())
280 static bool allowPopUp(ExecState* exec)
282 Frame* frame = Window::retrieveActive(exec)->impl()->frame();
285 if (frame->scriptProxy()->processingUserGesture())
287 Settings* settings = frame->settings();
288 return settings && settings->JavaScriptCanOpenWindowsAutomatically();
291 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
293 HashMap<String, String> map;
295 Vector<String> features;
296 featuresArg.split(';', features);
297 Vector<String>::const_iterator end = features.end();
298 for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
300 int pos = s.find('=');
301 int colonPos = s.find(':');
302 if (pos >= 0 && colonPos >= 0)
303 continue; // ignore any strings that have both = and :
307 // null string for value means key without value
308 map.set(s.stripWhiteSpace().lower(), String());
310 String key = s.left(pos).stripWhiteSpace().lower();
311 String val = s.substring(pos + 1).stripWhiteSpace().lower();
312 int spacePos = val.find(' ');
314 val = val.left(spacePos);
322 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
323 const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
325 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
327 ResourceRequest request;
329 request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
330 FrameLoadRequest frameRequest(request, frameName);
334 // We need to use the active frame's loader to let FrameLoader know
335 // which principal is requesting the navigation. Unfortunately, there
336 // might not be an activeFrame, in which case we resort to using the
339 // See http://bugs.webkit.org/show_bug.cgi?id=16522
340 loader = activeFrame->loader();
342 loader = openerFrame->loader();
344 // FIXME: It's much better for client API if a new window starts with a URL, here where we
345 // know what URL we are going to open. Unfortunately, this code passes the empty string
346 // for the URL, but there's a reason for that. Before loading we have to set up the opener,
347 // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
348 // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
349 // We'd have to resolve all those issues to pass the URL instead of "".
352 Frame* newFrame = loader->createWindow(frameRequest, windowFeatures, created);
356 newFrame->loader()->setOpener(openerFrame);
357 newFrame->loader()->setOpenedByDOM();
359 Window* newWindow = Window::retrieveWindow(newFrame);
362 newWindow->putDirect("dialogArguments", dialogArgs);
364 if (!protocolIs(url, "javascript") || newWindow->allowsAccessFrom(exec)) {
365 KURL completedURL = url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url);
366 bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
369 newFrame->loader()->changeLocation(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
370 if (Document* oldDoc = openerFrame->document())
371 newFrame->document()->setBaseURL(oldDoc->baseURL());
372 } else if (!url.isEmpty())
373 newFrame->loader()->scheduleLocationChange(completedURL.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
379 static bool canShowModalDialog(const Frame* frame)
383 return frame->page()->chrome()->canRunModal();
386 static bool canShowModalDialogNow(const Frame* frame)
390 return frame->page()->chrome()->canRunModalNow();
393 static JSValue* showModalDialog(ExecState* exec, Frame* frame, const String& url, JSValue* dialogArgs, const String& featureArgs)
395 if (!canShowModalDialogNow(frame) || !allowPopUp(exec))
396 return jsUndefined();
398 const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
400 const bool trusted = false;
402 // The following features from Microsoft's documentation are not implemented:
403 // - default font settings
404 // - width, height, left, and top specified in units other than "px"
405 // - edge (sunken or raised, default is raised)
406 // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
407 // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
408 // - unadorned: trusted && boolFeature(features, "unadorned");
411 return jsUndefined();
413 FloatRect screenRect = screenAvailableRect(frame->view());
415 WindowFeatures wargs;
416 wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
417 wargs.widthSet = true;
418 wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
419 wargs.heightSet = true;
421 wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
422 wargs.xSet = wargs.x > 0;
423 wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
424 wargs.ySet = wargs.y > 0;
426 if (WindowFeatures::boolFeature(features, "center", true)) {
428 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
432 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
438 wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
439 wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
440 wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
441 wargs.menuBarVisible = false;
442 wargs.toolBarVisible = false;
443 wargs.locationBarVisible = false;
444 wargs.fullscreen = false;
446 Frame* dialogFrame = createWindow(exec, frame, url, "", wargs, dialogArgs);
448 return jsUndefined();
450 Window* dialogWindow = Window::retrieveWindow(dialogFrame);
452 // Get the return value either just before clearing the dialog window's
453 // properties (in Window::clear), or when on return from runModal.
454 JSValue* returnValue = 0;
455 dialogWindow->setReturnValueSlot(&returnValue);
456 dialogFrame->page()->chrome()->runModal();
457 dialogWindow->setReturnValueSlot(0);
459 // If we don't have a return value, get it now.
460 // Either Window::clear was not called yet, or there was no return value,
461 // and in that case, there's no harm in trying again (no benefit either).
463 returnValue = dialogWindow->getDirect("returnValue");
465 return returnValue ? returnValue : jsUndefined();
468 JSValue *Window::getValueProperty(ExecState *exec, int token) const
470 ASSERT(impl()->frame());
474 return jsUndefined(); // FIXME: implement this
476 if (!allowsAccessFrom(exec))
477 return jsUndefined();
479 return jsUndefined();
480 return toJS(exec, d->m_evt);
484 case ClientInformation: {
485 if (!allowsAccessFrom(exec))
486 return jsUndefined();
487 // Store the navigator in the object so we get the same one each time.
488 Navigator* n = new Navigator(exec->lexicalGlobalObject()->objectPrototype(), impl()->frame());
489 // FIXME: this will make the "navigator" object accessible from windows that fail
490 // the security check the first time, but not subsequent times, seems weird.
491 const_cast<Window *>(this)->putDirect("navigator", n, DontDelete);
492 const_cast<Window *>(this)->putDirect("clientInformation", n, DontDelete);
496 if (!allowsAccessFrom(exec))
497 return jsUndefined();
498 // FIXME: this property (and the few below) probably shouldn't create a new object every
500 return new ImageConstructorImp(exec, impl()->frame()->document());
502 if (!allowsAccessFrom(exec))
503 return jsUndefined();
504 return new JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
506 if (!allowsAccessFrom(exec))
507 return jsUndefined();
508 return new JSXMLHttpRequestConstructorImp(exec, impl()->frame()->document());
511 if (!allowsAccessFrom(exec))
512 return jsUndefined();
513 if (!MediaPlayer::isAvailable())
514 return jsUndefined();
515 return new JSAudioConstructor(exec, impl()->frame()->document());
517 return jsUndefined();
521 if (!allowsAccessFrom(exec))
522 return jsUndefined();
523 return new XSLTProcessorConstructorImp(exec);
526 return jsUndefined();
530 if (!allowsAccessFrom(exec))
531 return jsUndefined();
535 return getListener(exec, abortEvent);
537 return getListener(exec, blurEvent);
539 return getListener(exec, changeEvent);
541 return getListener(exec, clickEvent);
543 return getListener(exec, dblclickEvent);
545 return getListener(exec, errorEvent);
547 return getListener(exec, focusEvent);
549 return getListener(exec, keydownEvent);
551 return getListener(exec, keypressEvent);
553 return getListener(exec, keyupEvent);
555 return getListener(exec, loadEvent);
557 return getListener(exec, mousedownEvent);
559 return getListener(exec, mousemoveEvent);
561 return getListener(exec, mouseoutEvent);
563 return getListener(exec, mouseoverEvent);
565 return getListener(exec, mouseupEvent);
566 case OnWindowMouseWheel:
567 return getListener(exec, mousewheelEvent);
569 return getListener(exec, resetEvent);
571 return getListener(exec,resizeEvent);
573 return getListener(exec,scrollEvent);
575 return getListener(exec,searchEvent);
577 return getListener(exec,selectEvent);
579 return getListener(exec,submitEvent);
581 return getListener(exec, beforeunloadEvent);
583 return getListener(exec, unloadEvent);
585 ASSERT_NOT_REACHED();
586 return jsUndefined();
589 JSValue* Window::childFrameGetter(ExecState*, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
591 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName)));
594 JSValue* Window::indexGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot& slot)
596 return retrieve(static_cast<Window*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index()));
599 JSValue* Window::namedItemGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
601 Window* thisObj = static_cast<Window*>(slot.slotBase());
602 Document* doc = thisObj->impl()->frame()->document();
603 ASSERT(thisObj->allowsAccessFrom(exec));
605 ASSERT(doc->isHTMLDocument());
607 RefPtr<WebCore::HTMLCollection> collection = doc->windowNamedItems(propertyName);
608 if (collection->length() == 1)
609 return toJS(exec, collection->firstItem());
610 return toJS(exec, collection.get());
613 bool Window::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
615 // Check for child frames by name before built-in properties to
616 // match Mozilla. This does not match IE, but some sites end up
617 // naming frames things that conflict with window properties that
618 // are in Moz but not IE. Since we have some of these, we have to do
620 if (impl()->frame()->tree()->child(propertyName)) {
621 slot.setCustom(this, childFrameGetter);
625 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
627 if (entry->attr & Function) {
628 if (entry->value.functionValue == windowProtoFuncShowModalDialog) {
629 if (!canShowModalDialog(impl()->frame()))
632 if (allowsAccessFrom(exec))
633 slot.setStaticEntry(this, entry, staticFunctionGetter);
635 slot.setUndefined(this);
637 slot.setStaticEntry(this, entry, staticValueGetter<Window>);
641 // Do prototype lookup early so that functions and attributes in the prototype can have
642 // precedence over the index and name getters.
643 JSValue* proto = prototype();
644 if (proto->isObject()) {
645 if (static_cast<JSObject*>(proto)->getOwnPropertySlot(exec, propertyName, slot)) {
646 if (!allowsAccessFrom(exec))
647 slot.setUndefined(this);
652 // FIXME: Search the whole frame hierachy somewhere around here.
653 // We need to test the correct priority order.
655 // allow window[1] or parent[1] etc. (#56983)
657 unsigned i = propertyName.toArrayIndex(&ok);
658 if (ok && i < impl()->frame()->tree()->childCount()) {
659 slot.setCustomIndex(this, i, indexGetter);
663 if (!allowsAccessFrom(exec)) {
664 slot.setUndefined(this);
668 // Allow shortcuts like 'Image1' instead of document.images.Image1
669 Document* doc = impl()->frame()->document();
670 if (doc && doc->isHTMLDocument()) {
671 AtomicString atomicPropertyName = propertyName;
672 if (static_cast<HTMLDocument*>(doc)->hasNamedItem(atomicPropertyName) || doc->getElementById(atomicPropertyName)) {
673 slot.setCustom(this, namedItemGetter);
678 return Base::getOwnPropertySlot(exec, propertyName, slot);
681 void Window::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
683 const HashEntry* entry = Lookup::findEntry(&WindowTable, propertyName);
685 if (entry->attr & Function) {
686 if (allowsAccessFrom(exec))
687 Base::put(exec, propertyName, value);
690 if (entry->attr & ReadOnly)
693 switch (entry->value.intValue) {
695 if (Frame* p = Window::retrieveActive(exec)->impl()->frame()) {
696 // To avoid breaking old widgets, make "var location =" in a top-level frame create
697 // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
698 if (Settings* settings = p->settings()) {
699 if (settings->usesDashboardBackwardCompatibilityMode() && !p->tree()->parent()) {
700 if (allowsAccessFrom(exec))
701 putDirect(propertyName, value);
706 if (!p->loader()->shouldAllowNavigation(impl()->frame()))
708 String dstUrl = p->loader()->completeURL(value->toString(exec)).string();
709 if (!protocolIs(dstUrl, "javascript") || allowsAccessFrom(exec)) {
710 bool userGesture = p->scriptProxy()->processingUserGesture();
711 // We want a new history item if this JS was called via a user gesture
712 impl()->frame()->loader()->scheduleLocationChange(dstUrl, p->loader()->outgoingReferrer(), false, userGesture);
718 if (allowsAccessFrom(exec))
719 setListener(exec, abortEvent,value);
722 if (allowsAccessFrom(exec))
723 setListener(exec, blurEvent,value);
726 if (allowsAccessFrom(exec))
727 setListener(exec, changeEvent,value);
730 if (allowsAccessFrom(exec))
731 setListener(exec,clickEvent,value);
734 if (allowsAccessFrom(exec))
735 setListener(exec, dblclickEvent,value);
738 if (allowsAccessFrom(exec))
739 setListener(exec, errorEvent, value);
742 if (allowsAccessFrom(exec))
743 setListener(exec,focusEvent,value);
746 if (allowsAccessFrom(exec))
747 setListener(exec,keydownEvent,value);
750 if (allowsAccessFrom(exec))
751 setListener(exec,keypressEvent,value);
754 if (allowsAccessFrom(exec))
755 setListener(exec,keyupEvent,value);
758 if (allowsAccessFrom(exec))
759 setListener(exec,loadEvent,value);
762 if (allowsAccessFrom(exec))
763 setListener(exec,mousedownEvent,value);
766 if (allowsAccessFrom(exec))
767 setListener(exec,mousemoveEvent,value);
770 if (allowsAccessFrom(exec))
771 setListener(exec,mouseoutEvent,value);
774 if (allowsAccessFrom(exec))
775 setListener(exec,mouseoverEvent,value);
778 if (allowsAccessFrom(exec))
779 setListener(exec,mouseupEvent,value);
781 case OnWindowMouseWheel:
782 if (allowsAccessFrom(exec))
783 setListener(exec, mousewheelEvent,value);
786 if (allowsAccessFrom(exec))
787 setListener(exec,resetEvent,value);
790 if (allowsAccessFrom(exec))
791 setListener(exec,resizeEvent,value);
794 if (allowsAccessFrom(exec))
795 setListener(exec,scrollEvent,value);
798 if (allowsAccessFrom(exec))
799 setListener(exec,searchEvent,value);
802 if (allowsAccessFrom(exec))
803 setListener(exec,selectEvent,value);
806 if (allowsAccessFrom(exec))
807 setListener(exec,submitEvent,value);
810 if (allowsAccessFrom(exec))
811 setListener(exec, beforeunloadEvent, value);
814 if (allowsAccessFrom(exec))
815 setListener(exec, unloadEvent, value);
821 if (allowsAccessFrom(exec))
822 Base::put(exec, propertyName, value);
825 bool Window::allowsAccessFrom(const JSGlobalObject* other) const
827 SecurityOrigin::Reason reason;
828 if (allowsAccessFromPrivate(other, reason))
830 printErrorMessage(crossDomainAccessErrorMessage(other, reason));
834 bool Window::allowsAccessFrom(ExecState* exec) const
836 SecurityOrigin::Reason reason;
837 if (allowsAccessFromPrivate(exec, reason))
839 printErrorMessage(crossDomainAccessErrorMessage(exec->dynamicGlobalObject(), reason));
843 bool Window::allowsAccessFromNoErrorMessage(ExecState* exec) const
845 SecurityOrigin::Reason reason;
846 return allowsAccessFromPrivate(exec, reason);
849 bool Window::allowsAccessFrom(ExecState* exec, String& message) const
851 SecurityOrigin::Reason reason;
852 if (allowsAccessFromPrivate(exec, reason))
854 message = crossDomainAccessErrorMessage(exec->dynamicGlobalObject(), reason);
858 ALWAYS_INLINE bool Window::allowsAccessFromPrivate(const ExecState* exec, SecurityOrigin::Reason& reason) const
860 if (allowsAccessFromPrivate(exec->dynamicGlobalObject(), reason))
862 if (reason == SecurityOrigin::DomainSetInDOMMismatch) {
863 // If the only reason the access failed was a domainSetInDOM bit mismatch, try again against
864 // lexical global object <rdar://problem/5698200>
865 if (allowsAccessFromPrivate(exec->lexicalGlobalObject(), reason))
871 ALWAYS_INLINE bool Window::allowsAccessFromPrivate(const JSGlobalObject* other, SecurityOrigin::Reason& reason) const
873 const Frame* originFrame = static_cast<const Window*>(other)->impl()->frame();
875 reason = SecurityOrigin::GenericMismatch;
879 const Frame* targetFrame = impl()->frame();
881 if (originFrame == targetFrame)
885 reason = SecurityOrigin::GenericMismatch;
889 WebCore::Document* targetDocument = targetFrame->document();
891 // JS may be attempting to access the "window" object, which should be valid,
892 // even if the document hasn't been constructed yet. If the document doesn't
893 // exist yet allow JS to access the window object.
897 WebCore::Document* originDocument = originFrame->document();
899 const SecurityOrigin* originSecurityOrigin = originDocument->securityOrigin();
900 const SecurityOrigin* targetSecurityOrigin = targetDocument->securityOrigin();
902 if (originSecurityOrigin->canAccess(targetSecurityOrigin, reason))
908 String Window::crossDomainAccessErrorMessage(const JSGlobalObject* other, SecurityOrigin::Reason) const
910 const Frame* originFrame = static_cast<const Window*>(other)->impl()->frame();
911 const Frame* targetFrame = impl()->frame();
912 if (!originFrame || !targetFrame)
914 WebCore::Document* targetDocument = targetFrame->document();
915 WebCore::Document* originDocument = originFrame->document();
916 if (!originDocument || !targetDocument)
918 // FIXME: this error message should contain more specifics of why the same origin check has failed.
919 return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
920 targetDocument->url().string().utf8().data(), originDocument->url().string().utf8().data());
923 void Window::printErrorMessage(const String& message) const
925 if (message.isEmpty())
928 Frame* frame = impl()->frame();
932 if (frame->settings()->privateBrowsingEnabled())
935 if (Interpreter::shouldPrintExceptions())
936 printf("%s", message.utf8().data());
938 if (Page* page = frame->page())
939 page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
942 ExecState* Window::globalExec()
944 // We need to make sure that any script execution happening in this
945 // frame does not destroy it
946 ASSERT(impl()->frame());
947 impl()->frame()->keepAlive();
948 return Base::globalExec();
951 bool Window::shouldInterruptScript() const
953 ASSERT(impl()->frame());
954 Page* page = impl()->frame()->page();
956 // See <rdar://problem/5479443>. We don't think that page can ever be NULL
957 // in this case, but if it is, we've gotten into a state where we may have
958 // hung the UI, with no way to ask the client whether to cancel execution.
959 // For now, our solution is just to cancel execution no matter what,
960 // ensuring that we never hang. We might want to consider other solutions
961 // if we discover problems with this one.
966 return page->chrome()->shouldInterruptJavaScript();
969 void Window::setListener(ExecState* exec, const AtomicString& eventType, JSValue* func)
971 ASSERT(impl()->frame());
972 Document* doc = impl()->frame()->document();
976 doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(func, true));
979 JSValue* Window::getListener(ExecState* exec, const AtomicString& eventType) const
981 ASSERT(impl()->frame());
982 Document* doc = impl()->frame()->document();
984 return jsUndefined();
986 WebCore::EventListener* listener = doc->getHTMLWindowEventListener(eventType);
987 if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
988 return static_cast<JSEventListener*>(listener)->listenerObj();
992 JSEventListener* Window::findJSEventListener(JSValue* val, bool html)
994 if (!val->isObject())
996 JSObject* object = static_cast<JSObject*>(val);
997 ListenersMap& listeners = html ? d->jsHTMLEventListeners : d->jsEventListeners;
998 return listeners.get(object);
1001 JSEventListener* Window::findOrCreateJSEventListener(JSValue* val, bool html)
1003 JSEventListener* listener = findJSEventListener(val, html);
1007 if (!val->isObject())
1009 JSObject* object = static_cast<JSObject*>(val);
1011 // Note that the JSEventListener constructor adds it to our jsEventListeners list
1012 return new JSEventListener(object, this, html);
1015 JSUnprotectedEventListener* Window::findJSUnprotectedEventListener(JSValue* val, bool html)
1017 if (!val->isObject())
1019 JSObject* object = static_cast<JSObject*>(val);
1020 UnprotectedListenersMap& listeners = html ? d->jsUnprotectedHTMLEventListeners : d->jsUnprotectedEventListeners;
1021 return listeners.get(object);
1024 JSUnprotectedEventListener* Window::findOrCreateJSUnprotectedEventListener(JSValue* val, bool html)
1026 JSUnprotectedEventListener* listener = findJSUnprotectedEventListener(val, html);
1029 if (!val->isObject())
1031 JSObject* object = static_cast<JSObject*>(val);
1033 // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
1034 return new JSUnprotectedEventListener(object, this, html);
1037 void Window::clearHelperObjectProperties()
1043 void Window::clear()
1047 if (d->m_returnValueSlot && !*d->m_returnValueSlot)
1048 *d->m_returnValueSlot = getDirect("returnValue");
1051 clearHelperObjectProperties();
1053 // Now recreate a working global object for the next URL that will use us; but only if we haven't been
1055 if (Frame* frame = impl()->frame())
1056 frame->scriptProxy()->globalObject()->reset(JSDOMWindowPrototype::self());
1058 // there's likely to be lots of garbage now
1059 gcController().garbageCollectSoon();
1062 void Window::setCurrentEvent(Event* evt)
1067 Event* Window::currentEvent()
1072 JSValue* windowProtoFuncAToB(ExecState* exec, JSObject* thisObj, const List& args)
1074 if (!thisObj->inherits(&Window::info))
1075 return throwError(exec, TypeError);
1076 if (!static_cast<Window*>(thisObj)->allowsAccessFrom(exec))
1077 return jsUndefined();
1079 if (args.size() < 1)
1080 return throwError(exec, SyntaxError, "Not enough arguments");
1082 JSValue* v = args[0];
1086 UString s = v->toString(exec);
1088 setDOMException(exec, INVALID_CHARACTER_ERR);
1089 return jsUndefined();
1092 Vector<char> in(s.size());
1093 for (int i = 0; i < s.size(); ++i)
1094 in[i] = static_cast<char>(s.data()[i].unicode());
1097 if (!base64Decode(in, out))
1098 return throwError(exec, GeneralError, "Cannot decode base64");
1100 return jsString(String(out.data(), out.size()));
1103 JSValue* windowProtoFuncBToA(ExecState* exec, JSObject* thisObj, const List& args)
1105 if (!thisObj->inherits(&Window::info))
1106 return throwError(exec, TypeError);
1107 if (!static_cast<Window*>(thisObj)->allowsAccessFrom(exec))
1108 return jsUndefined();
1110 if (args.size() < 1)
1111 return throwError(exec, SyntaxError, "Not enough arguments");
1113 JSValue* v = args[0];
1117 UString s = v->toString(exec);
1119 setDOMException(exec, INVALID_CHARACTER_ERR);
1120 return jsUndefined();
1123 Vector<char> in(s.size());
1124 for (int i = 0; i < s.size(); ++i)
1125 in[i] = static_cast<char>(s.data()[i].unicode());
1128 base64Encode(in, out);
1130 return jsString(String(out.data(), out.size()));
1133 JSValue* windowProtoFuncOpen(ExecState* exec, JSObject* thisObj, const List& args)
1135 if (!thisObj->inherits(&Window::info))
1136 return throwError(exec, TypeError);
1137 Window* window = static_cast<Window*>(thisObj);
1138 if (!window->allowsAccessFrom(exec))
1139 return jsUndefined();
1141 Frame* frame = window->impl()->frame();
1143 return jsUndefined();
1144 Frame* activeFrame = Window::retrieveActive(exec)->impl()->frame();
1146 return jsUndefined();
1148 Page* page = frame->page();
1150 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args[0]);
1151 AtomicString frameName = args[1]->isUndefinedOrNull() ? "_blank" : AtomicString(args[1]->toString(exec));
1153 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1154 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1155 if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1156 return jsUndefined();
1158 // Get the target frame for the special cases of _top and _parent. In those
1159 // cases, we can schedule a location change right now and return early.
1160 bool topOrParent = false;
1161 if (frameName == "_top") {
1162 frame = frame->tree()->top();
1164 } else if (frameName == "_parent") {
1165 if (Frame* parent = frame->tree()->parent())
1170 if (!activeFrame->loader()->shouldAllowNavigation(frame))
1171 return jsUndefined();
1173 String completedURL;
1174 if (!urlString.isEmpty())
1175 completedURL = activeFrame->document()->completeURL(urlString).string();
1177 const Window* targetedWindow = Window::retrieveWindow(frame);
1178 if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
1179 bool userGesture = activeFrame->scriptProxy()->processingUserGesture();
1180 frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1182 return Window::retrieve(frame);
1185 // In the case of a named frame or a new window, we'll use the createWindow() helper
1186 WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args[2]));
1187 FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1188 WebCore::DOMWindow::adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), windowRect, windowRect);
1190 windowFeatures.x = windowRect.x();
1191 windowFeatures.y = windowRect.y();
1192 windowFeatures.height = windowRect.height();
1193 windowFeatures.width = windowRect.width();
1195 frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1198 return jsUndefined();
1200 return Window::retrieve(frame); // global object
1203 JSValue* windowProtoFuncSetTimeout(ExecState* exec, JSObject* thisObj, const List& args)
1205 if (!thisObj->inherits(&Window::info))
1206 return throwError(exec, TypeError);
1207 Window* window = static_cast<Window*>(thisObj);
1208 if (!window->allowsAccessFrom(exec))
1209 return jsUndefined();
1211 JSValue* v = args[0];
1213 return jsNumber(window->installTimeout(v->toString(exec), args[1]->toInt32(exec), true /*single shot*/));
1214 if (v->isObject() && static_cast<JSObject*>(v)->implementsCall()) {
1216 args.getSlice(2, argsTail);
1217 return jsNumber(window->installTimeout(v, argsTail, args[1]->toInt32(exec), true /*single shot*/));
1220 return jsUndefined();
1223 JSValue* windowProtoFuncClearTimeout(ExecState* exec, JSObject* thisObj, const List& args)
1225 // Also the implementation for window.clearInterval()
1227 if (!thisObj->inherits(&Window::info))
1228 return throwError(exec, TypeError);
1229 Window* window = static_cast<Window*>(thisObj);
1230 if (!window->allowsAccessFrom(exec))
1231 return jsUndefined();
1233 window->clearTimeout(args[0]->toInt32(exec));
1234 return jsUndefined();
1237 JSValue* windowProtoFuncSetInterval(ExecState* exec, JSObject* thisObj, const List& args)
1239 if (!thisObj->inherits(&Window::info))
1240 return throwError(exec, TypeError);
1241 Window* window = static_cast<Window*>(thisObj);
1242 if (!window->allowsAccessFrom(exec))
1243 return jsUndefined();
1245 if (args.size() >= 2) {
1246 JSValue* v = args[0];
1247 int delay = args[1]->toInt32(exec);
1249 return jsNumber(window->installTimeout(v->toString(exec), delay, false));
1250 if (v->isObject() && static_cast<JSObject*>(v)->implementsCall()) {
1252 args.getSlice(2, argsTail);
1253 return jsNumber(window->installTimeout(v, argsTail, delay, false));
1257 return jsUndefined();
1261 JSValue* windowProtoFuncAddEventListener(ExecState* exec, JSObject* thisObj, const List& args)
1263 if (!thisObj->inherits(&Window::info))
1264 return throwError(exec, TypeError);
1265 Window* window = static_cast<Window*>(thisObj);
1266 if (!window->allowsAccessFrom(exec))
1267 return jsUndefined();
1268 Frame* frame = window->impl()->frame();
1270 return jsUndefined();
1272 if (JSEventListener* listener = window->findOrCreateJSEventListener(args[1])) {
1273 if (Document* doc = frame->document())
1274 doc->addWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1277 return jsUndefined();
1280 JSValue* windowProtoFuncRemoveEventListener(ExecState* exec, JSObject* thisObj, const List& args)
1282 if (!thisObj->inherits(&Window::info))
1283 return throwError(exec, TypeError);
1284 Window* window = static_cast<Window*>(thisObj);
1285 if (!window->allowsAccessFrom(exec))
1286 return jsUndefined();
1287 Frame* frame = window->impl()->frame();
1289 return jsUndefined();
1291 if (JSEventListener* listener = window->findJSEventListener(args[1])) {
1292 if (Document* doc = frame->document())
1293 doc->removeWindowEventListener(AtomicString(args[0]->toString(exec)), listener, args[2]->toBoolean(exec));
1296 return jsUndefined();
1299 JSValue* windowProtoFuncShowModalDialog(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 return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args[0]), args[1], valueToStringWithUndefinedOrNullCheck(exec, args[2]));
1311 JSValue* windowProtoFuncNotImplemented(ExecState* exec, JSObject* thisObj, const List& args)
1313 if (!thisObj->inherits(&Window::info))
1314 return throwError(exec, TypeError);
1316 return jsUndefined();
1319 void Window::setReturnValueSlot(JSValue** slot)
1321 d->m_returnValueSlot = slot;
1324 ////////////////////// timeouts ////////////////////////
1326 void Window::clearAllTimeouts()
1328 deleteAllValues(d->m_timeouts);
1329 d->m_timeouts.clear();
1332 int Window::installTimeout(WebCore::ScheduledAction* a, int t, bool singleShot)
1334 int timeoutId = ++lastUsedTimeoutId;
1336 // avoid wraparound going negative on us
1340 int nestLevel = timerNestingLevel + 1;
1341 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1342 ASSERT(!d->m_timeouts.get(timeoutId));
1343 d->m_timeouts.set(timeoutId, timer);
1344 // Use a minimum interval of 10 ms to match other browsers, but only once we've
1345 // nested enough to notice that we're repeating.
1346 // Faster timers might be "better", but they're incompatible.
1347 double interval = max(0.001, t * 0.001);
1348 if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1349 interval = cMinimumTimerInterval;
1351 timer->startOneShot(interval);
1353 timer->startRepeating(interval);
1357 int Window::installTimeout(const UString& handler, int t, bool singleShot)
1359 return installTimeout(new WebCore::ScheduledAction(handler), t, singleShot);
1362 int Window::installTimeout(JSValue* func, const List& args, int t, bool singleShot)
1364 return installTimeout(new WebCore::ScheduledAction(func, args), t, singleShot);
1367 WebCore::PausedTimeouts* Window::pauseTimeouts()
1369 size_t count = d->m_timeouts.size();
1373 PausedTimeout* t = new PausedTimeout [count];
1374 PausedTimeouts* result = new PausedTimeouts(t, count);
1376 WindowPrivate::TimeoutsMap::iterator it = d->m_timeouts.begin();
1377 for (size_t i = 0; i != count; ++i, ++it) {
1378 int timeoutId = it->first;
1379 DOMWindowTimer* timer = it->second;
1380 t[i].timeoutId = timeoutId;
1381 t[i].nestingLevel = timer->nestingLevel();
1382 t[i].nextFireInterval = timer->nextFireInterval();
1383 t[i].repeatInterval = timer->repeatInterval();
1384 t[i].action = timer->takeAction();
1386 ASSERT(it == d->m_timeouts.end());
1388 deleteAllValues(d->m_timeouts);
1389 d->m_timeouts.clear();
1394 void Window::resumeTimeouts(PausedTimeouts* timeouts)
1398 size_t count = timeouts->numTimeouts();
1399 PausedTimeout* array = timeouts->takeTimeouts();
1400 for (size_t i = 0; i != count; ++i) {
1401 int timeoutId = array[i].timeoutId;
1402 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1403 d->m_timeouts.set(timeoutId, timer);
1404 timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1409 void Window::clearTimeout(int timeoutId, bool delAction)
1411 // timeout IDs have to be positive, and 0 and -1 are unsafe to
1412 // even look up since they are the empty and deleted value
1417 delete d->m_timeouts.take(timeoutId);
1420 void Window::timerFired(DOMWindowTimer* timer)
1422 // Simple case for non-one-shot timers.
1423 if (timer->isActive()) {
1424 int timeoutId = timer->timeoutId();
1426 timer->action()->execute(this);
1427 // The DOMWindowTimer object may have been deleted or replaced during execution,
1428 // so we re-fetch it.
1429 timer = d->m_timeouts.get(timeoutId);
1433 if (timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1434 timer->setNestingLevel(timer->nestingLevel() + 1);
1435 if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1436 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1441 // Delete timer before executing the action for one-shot timers.
1442 WebCore::ScheduledAction* action = timer->takeAction();
1443 d->m_timeouts.remove(timer->timeoutId());
1445 action->execute(this);
1451 void Window::disconnectFrame()
1455 d->loc->m_frame = 0;
1458 Window::ListenersMap& Window::jsEventListeners()
1460 return d->jsEventListeners;
1463 Window::ListenersMap& Window::jsHTMLEventListeners()
1465 return d->jsHTMLEventListeners;
1468 Window::UnprotectedListenersMap& Window::jsUnprotectedEventListeners()
1470 return d->jsUnprotectedEventListeners;
1473 Window::UnprotectedListenersMap& Window::jsUnprotectedHTMLEventListeners()
1475 return d->jsUnprotectedHTMLEventListeners;
1478 /////////////////////////////////////////////////////////////////////////////
1480 void DOMWindowTimer::fired()
1482 timerNestingLevel = m_nestingLevel;
1483 m_object->timerFired(this);
1484 timerNestingLevel = 0;
1489 using namespace KJS;
1493 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1497 Frame* frame = domWindow->frame();
1500 return Window::retrieve(frame);
1503 } // namespace WebCore