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 "JSDOMWindowBase.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 "JSAudioConstructor.h"
42 #include "JSDOMWindowCustom.h"
44 #include "JSEventListener.h"
45 #include "JSHTMLCollection.h"
46 #include "JSHTMLOptionElementConstructor.h"
47 #include "JSImageConstructor.h"
49 #include "JSXMLHttpRequestConstructor.h"
51 #include "MediaPlayer.h"
53 #include "PausedTimeouts.h"
54 #include "PlatformScreen.h"
55 #include "PluginInfoStore.h"
56 #include "RenderView.h"
57 #include "ScheduledAction.h"
58 #include "ScriptController.h"
59 #include "SecurityOrigin.h"
61 #include "WindowFeatures.h"
62 #include "htmlediting.h"
63 #include <kjs/Error.h>
64 #include <kjs/JSLock.h>
65 #include <wtf/AlwaysInline.h>
66 #include <wtf/MathExtras.h>
69 #include "JSXSLTProcessorConstructor.h"
76 static JSValue* windowProtoFuncAToB(ExecState*, JSObject*, JSValue*, const ArgList&);
77 static JSValue* windowProtoFuncBToA(ExecState*, JSObject*, JSValue*, const ArgList&);
78 static JSValue* windowProtoFuncOpen(ExecState*, JSObject*, JSValue*, const ArgList&);
79 static JSValue* windowProtoFuncSetTimeout(ExecState*, JSObject*, JSValue*, const ArgList&);
80 static JSValue* windowProtoFuncClearTimeoutOrInterval(ExecState*, JSObject*, JSValue*, const ArgList&);
81 static JSValue* windowProtoFuncSetInterval(ExecState*, JSObject*, JSValue*, const ArgList&);
82 static JSValue* windowProtoFuncAddEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
83 static JSValue* windowProtoFuncRemoveEventListener(ExecState*, JSObject*, JSValue*, const ArgList&);
84 static JSValue* windowProtoFuncShowModalDialog(ExecState*, JSObject*, JSValue*, const ArgList&);
85 static JSValue* windowProtoFuncNotImplemented(ExecState*, JSObject*, JSValue*, const ArgList&);
89 #include "JSDOMWindowBase.lut.h"
93 using namespace EventNames;
95 static int lastUsedTimeoutId;
97 static int timerNestingLevel = 0;
98 const int cMaxTimerNestingLevel = 5;
99 const double cMinimumTimerInterval = 0.010;
101 class DOMWindowTimer : public TimerBase {
103 DOMWindowTimer(int timeoutId, int nestingLevel, JSDOMWindowBase* object, ScheduledAction* action)
104 : m_timeoutId(timeoutId)
105 , m_nestingLevel(nestingLevel)
111 virtual ~DOMWindowTimer()
117 int timeoutId() const { return m_timeoutId; }
119 int nestingLevel() const { return m_nestingLevel; }
120 void setNestingLevel(int n) { m_nestingLevel = n; }
122 ScheduledAction* action() const { return m_action; }
123 ScheduledAction* takeAction() { ScheduledAction* a = m_action; m_action = 0; return a; }
126 virtual void fired();
130 JSDOMWindowBase* m_object;
131 ScheduledAction* m_action;
134 ////////////////////// JSDOMWindowBase Object ////////////////////////
136 const ClassInfo JSDOMWindowBase::s_info = { "Window", 0, &JSDOMWindowBaseTable, 0 };
139 @begin JSDOMWindowBaseTable
141 atob WebCore::windowProtoFuncAToB DontDelete|Function 1
142 btoa WebCore::windowProtoFuncBToA DontDelete|Function 1
143 open WebCore::windowProtoFuncOpen DontDelete|Function 3
144 setTimeout WebCore::windowProtoFuncSetTimeout DontDelete|Function 2
145 clearTimeout WebCore::windowProtoFuncClearTimeoutOrInterval DontDelete|Function 1
146 setInterval WebCore::windowProtoFuncSetInterval DontDelete|Function 2
147 clearInterval WebCore::windowProtoFuncClearTimeoutOrInterval DontDelete|Function 1
148 addEventListener WebCore::windowProtoFuncAddEventListener DontDelete|Function 3
149 removeEventListener WebCore::windowProtoFuncRemoveEventListener DontDelete|Function 3
150 showModalDialog WebCore::windowProtoFuncShowModalDialog DontDelete|Function 1
152 captureEvents WebCore::windowProtoFuncNotImplemented DontDelete|Function 0
153 releaseEvents WebCore::windowProtoFuncNotImplemented DontDelete|Function 0
156 crypto WebCore::JSDOMWindowBase::Crypto DontDelete|ReadOnly
157 event WebCore::JSDOMWindowBase::Event_ DontDelete
158 # -- Event Listeners --
159 onabort WebCore::JSDOMWindowBase::Onabort DontDelete
160 onblur WebCore::JSDOMWindowBase::Onblur DontDelete
161 onchange WebCore::JSDOMWindowBase::Onchange DontDelete
162 onclick WebCore::JSDOMWindowBase::Onclick DontDelete
163 ondblclick WebCore::JSDOMWindowBase::Ondblclick DontDelete
164 onerror WebCore::JSDOMWindowBase::Onerror DontDelete
165 onfocus WebCore::JSDOMWindowBase::Onfocus DontDelete
166 onkeydown WebCore::JSDOMWindowBase::Onkeydown DontDelete
167 onkeypress WebCore::JSDOMWindowBase::Onkeypress DontDelete
168 onkeyup WebCore::JSDOMWindowBase::Onkeyup DontDelete
169 onload WebCore::JSDOMWindowBase::Onload DontDelete
170 onmousedown WebCore::JSDOMWindowBase::Onmousedown DontDelete
171 onmousemove WebCore::JSDOMWindowBase::Onmousemove DontDelete
172 onmouseout WebCore::JSDOMWindowBase::Onmouseout DontDelete
173 onmouseover WebCore::JSDOMWindowBase::Onmouseover DontDelete
174 onmouseup WebCore::JSDOMWindowBase::Onmouseup DontDelete
175 onmousewheel WebCore::JSDOMWindowBase::OnWindowMouseWheel DontDelete
176 onreset WebCore::JSDOMWindowBase::Onreset DontDelete
177 onresize WebCore::JSDOMWindowBase::Onresize DontDelete
178 onscroll WebCore::JSDOMWindowBase::Onscroll DontDelete
179 onsearch WebCore::JSDOMWindowBase::Onsearch DontDelete
180 onselect WebCore::JSDOMWindowBase::Onselect DontDelete
181 onsubmit WebCore::JSDOMWindowBase::Onsubmit DontDelete
182 onunload WebCore::JSDOMWindowBase::Onunload DontDelete
183 onbeforeunload WebCore::JSDOMWindowBase::Onbeforeunload DontDelete
184 onwebkitanimationstart WebCore::JSDOMWindowBase::OnWebKitAnimationStart DontDelete
185 onwebkitanimationiteration WebCore::JSDOMWindowBase::OnWebKitAnimationIteration DontDelete
186 onwebkitanimationend WebCore::JSDOMWindowBase::OnWebKitAnimationEnd DontDelete
187 onwebkittransitionend WebCore::JSDOMWindowBase::OnWebKitTransitionEnd DontDelete
189 Audio WebCore::JSDOMWindowBase::Audio DontDelete
190 Image WebCore::JSDOMWindowBase::Image DontDelete
191 Option WebCore::JSDOMWindowBase::Option DontDelete
192 XMLHttpRequest WebCore::JSDOMWindowBase::XMLHttpRequest DontDelete
193 XSLTProcessor WebCore::JSDOMWindowBase::XSLTProcessor DontDelete
197 JSDOMWindowBase::JSDOMWindowBaseData::JSDOMWindowBaseData(PassRefPtr<DOMWindow> window_, JSDOMWindowBase* jsWindow_, JSDOMWindowShell* shell_)
198 : JSGlobalObjectData(jsWindow_, shell_)
206 JSDOMWindowBase::JSDOMWindowBase(JSObject* prototype, DOMWindow* window, JSDOMWindowShell* shell)
207 : JSGlobalObject(prototype, new JSDOMWindowBaseData(window, this, shell), shell)
209 // Time in milliseconds before the script timeout handler kicks in.
210 setTimeoutTime(10000);
212 GlobalPropertyInfo staticGlobals[] = {
213 GlobalPropertyInfo(Identifier(globalExec(), "document"), jsNull(), DontDelete | ReadOnly),
214 GlobalPropertyInfo(Identifier(globalExec(), "window"), d()->shell, DontDelete | ReadOnly)
217 addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo));
220 void JSDOMWindowBase::updateDocument()
222 ASSERT(d()->impl->document());
223 ExecState* exec = globalExec();
224 symbolTablePutWithAttributes(Identifier(exec, "document"), toJS(exec, d()->impl->document()), DontDelete | ReadOnly);
227 JSDOMWindowBase::~JSDOMWindowBase()
229 if (d()->impl->frame())
230 d()->impl->frame()->script()->clearFormerWindow(asJSDOMWindow(this));
234 // Clear any backpointers to the window
236 ListenersMap::iterator i2 = d()->jsEventListeners.begin();
237 ListenersMap::iterator e2 = d()->jsEventListeners.end();
238 for (; i2 != e2; ++i2)
239 i2->second->clearWindow();
240 i2 = d()->jsHTMLEventListeners.begin();
241 e2 = d()->jsHTMLEventListeners.end();
242 for (; i2 != e2; ++i2)
243 i2->second->clearWindow();
245 UnprotectedListenersMap::iterator i1 = d()->jsUnprotectedEventListeners.begin();
246 UnprotectedListenersMap::iterator e1 = d()->jsUnprotectedEventListeners.end();
247 for (; i1 != e1; ++i1)
248 i1->second->clearWindow();
249 i1 = d()->jsUnprotectedHTMLEventListeners.begin();
250 e1 = d()->jsUnprotectedHTMLEventListeners.end();
251 for (; i1 != e1; ++i1)
252 i1->second->clearWindow();
255 static bool allowPopUp(ExecState* exec)
257 Frame* frame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
260 if (frame->script()->processingUserGesture())
262 Settings* settings = frame->settings();
263 return settings && settings->JavaScriptCanOpenWindowsAutomatically();
266 static HashMap<String, String> parseModalDialogFeatures(const String& featuresArg)
268 HashMap<String, String> map;
270 Vector<String> features;
271 featuresArg.split(';', features);
272 Vector<String>::const_iterator end = features.end();
273 for (Vector<String>::const_iterator it = features.begin(); it != end; ++it) {
275 int pos = s.find('=');
276 int colonPos = s.find(':');
277 if (pos >= 0 && colonPos >= 0)
278 continue; // ignore any strings that have both = and :
282 // null string for value means key without value
283 map.set(s.stripWhiteSpace().lower(), String());
285 String key = s.left(pos).stripWhiteSpace().lower();
286 String val = s.substring(pos + 1).stripWhiteSpace().lower();
287 int spacePos = val.find(' ');
289 val = val.left(spacePos);
297 static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& url,
298 const String& frameName, const WindowFeatures& windowFeatures, JSValue* dialogArgs)
300 Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
303 ResourceRequest request;
305 request.setHTTPReferrer(activeFrame->loader()->outgoingReferrer());
306 FrameLoadRequest frameRequest(request, frameName);
308 // FIXME: It's much better for client API if a new window starts with a URL, here where we
309 // know what URL we are going to open. Unfortunately, this code passes the empty string
310 // for the URL, but there's a reason for that. Before loading we have to set up the opener,
311 // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
312 // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
313 // We'd have to resolve all those issues to pass the URL instead of "".
316 // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
317 // is different from the opener frame, and the name references a frame relative to the opener frame, for example
318 // "_self" or "_parent".
319 Frame* newFrame = activeFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
323 newFrame->loader()->setOpener(openerFrame);
324 newFrame->loader()->setOpenedByDOM();
326 JSDOMWindow* newWindow = toJSDOMWindow(newFrame);
329 newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
331 if (!protocolIs(url, "javascript") || newWindow->allowsAccessFrom(exec)) {
332 KURL completedURL = url.isEmpty() ? KURL("") : activeFrame->document()->completeURL(url);
333 bool userGesture = activeFrame->script()->processingUserGesture();
336 newFrame->loader()->changeLocation(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
337 else if (!url.isEmpty())
338 newFrame->loader()->scheduleLocationChange(completedURL.string(), activeFrame->loader()->outgoingReferrer(), false, userGesture);
344 static bool canShowModalDialog(const Frame* frame)
348 return frame->page()->chrome()->canRunModal();
351 static bool canShowModalDialogNow(const Frame* frame)
355 return frame->page()->chrome()->canRunModalNow();
358 static JSValue* showModalDialog(ExecState* exec, Frame* frame, const String& url, JSValue* dialogArgs, const String& featureArgs)
360 if (!canShowModalDialogNow(frame) || !allowPopUp(exec))
361 return jsUndefined();
363 const HashMap<String, String> features = parseModalDialogFeatures(featureArgs);
365 const bool trusted = false;
367 // The following features from Microsoft's documentation are not implemented:
368 // - default font settings
369 // - width, height, left, and top specified in units other than "px"
370 // - edge (sunken or raised, default is raised)
371 // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
372 // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
373 // - unadorned: trusted && boolFeature(features, "unadorned");
376 return jsUndefined();
378 FloatRect screenRect = screenAvailableRect(frame->view());
380 WindowFeatures wargs;
381 wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
382 wargs.widthSet = true;
383 wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
384 wargs.heightSet = true;
386 wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
387 wargs.xSet = wargs.x > 0;
388 wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
389 wargs.ySet = wargs.y > 0;
391 if (WindowFeatures::boolFeature(features, "center", true)) {
393 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
397 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
403 wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
404 wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
405 wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
406 wargs.menuBarVisible = false;
407 wargs.toolBarVisible = false;
408 wargs.locationBarVisible = false;
409 wargs.fullscreen = false;
411 Frame* dialogFrame = createWindow(exec, frame, url, "", wargs, dialogArgs);
413 return jsUndefined();
415 JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame);
417 // Get the return value either just before clearing the dialog window's
418 // properties (in JSDOMWindowBase::clear), or when on return from runModal.
419 JSValue* returnValue = 0;
420 dialogWindow->setReturnValueSlot(&returnValue);
421 dialogFrame->page()->chrome()->runModal();
422 dialogWindow->setReturnValueSlot(0);
424 // If we don't have a return value, get it now.
425 // Either JSDOMWindowBase::clear was not called yet, or there was no return value,
426 // and in that case, there's no harm in trying again (no benefit either).
428 returnValue = dialogWindow->getDirect(Identifier(exec, "returnValue"));
430 return returnValue ? returnValue : jsUndefined();
433 JSValue *JSDOMWindowBase::getValueProperty(ExecState *exec, int token) const
435 ASSERT(impl()->frame());
439 return jsUndefined(); // FIXME: implement this
441 if (!allowsAccessFrom(exec))
442 return jsUndefined();
444 return jsUndefined();
445 return toJS(exec, d()->evt);
447 if (!allowsAccessFrom(exec))
448 return jsUndefined();
449 // FIXME: this property (and the few below) probably shouldn't create a new object every
451 return new (exec) JSImageConstructor(exec, impl()->frame()->document());
453 if (!allowsAccessFrom(exec))
454 return jsUndefined();
455 return new (exec) JSHTMLOptionElementConstructor(exec, impl()->frame()->document());
457 if (!allowsAccessFrom(exec))
458 return jsUndefined();
459 return new (exec) JSXMLHttpRequestConstructor(exec, impl()->frame()->document());
462 if (!allowsAccessFrom(exec))
463 return jsUndefined();
464 if (!MediaPlayer::isAvailable())
465 return jsUndefined();
466 return new (exec) JSAudioConstructor(exec, impl()->frame()->document());
468 return jsUndefined();
472 if (!allowsAccessFrom(exec))
473 return jsUndefined();
474 return new (exec) JSXSLTProcessorConstructor(exec);
476 return jsUndefined();
480 if (!allowsAccessFrom(exec))
481 return jsUndefined();
485 return getListener(exec, abortEvent);
487 return getListener(exec, blurEvent);
489 return getListener(exec, changeEvent);
491 return getListener(exec, clickEvent);
493 return getListener(exec, dblclickEvent);
495 return getListener(exec, errorEvent);
497 return getListener(exec, focusEvent);
499 return getListener(exec, keydownEvent);
501 return getListener(exec, keypressEvent);
503 return getListener(exec, keyupEvent);
505 return getListener(exec, loadEvent);
507 return getListener(exec, mousedownEvent);
509 return getListener(exec, mousemoveEvent);
511 return getListener(exec, mouseoutEvent);
513 return getListener(exec, mouseoverEvent);
515 return getListener(exec, mouseupEvent);
516 case OnWindowMouseWheel:
517 return getListener(exec, mousewheelEvent);
519 return getListener(exec, resetEvent);
521 return getListener(exec,resizeEvent);
523 return getListener(exec,scrollEvent);
525 return getListener(exec,searchEvent);
527 return getListener(exec,selectEvent);
529 return getListener(exec,submitEvent);
531 return getListener(exec, beforeunloadEvent);
533 return getListener(exec, unloadEvent);
534 case OnWebKitAnimationStart:
535 return getListener(exec, webkitAnimationStartEvent);
536 case OnWebKitAnimationIteration:
537 return getListener(exec, webkitAnimationIterationEvent);
538 case OnWebKitAnimationEnd:
539 return getListener(exec, webkitAnimationEndEvent);
540 case OnWebKitTransitionEnd:
541 return getListener(exec, webkitTransitionEndEvent);
543 ASSERT_NOT_REACHED();
544 return jsUndefined();
547 JSValue* JSDOMWindowBase::childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
549 return toJS(exec, static_cast<JSDOMWindowBase*>(slot.slotBase())->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow());
552 JSValue* JSDOMWindowBase::indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
554 return toJS(exec, static_cast<JSDOMWindowBase*>(slot.slotBase())->impl()->frame()->tree()->child(slot.index())->domWindow());
557 JSValue* JSDOMWindowBase::namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
559 JSDOMWindowBase* thisObj = static_cast<JSDOMWindowBase*>(slot.slotBase());
560 Document* doc = thisObj->impl()->frame()->document();
561 ASSERT(thisObj->allowsAccessFrom(exec));
563 ASSERT(doc->isHTMLDocument());
565 RefPtr<HTMLCollection> collection = doc->windowNamedItems(propertyName);
566 if (collection->length() == 1)
567 return toJS(exec, collection->firstItem());
568 return toJS(exec, collection.get());
571 bool JSDOMWindowBase::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
573 // Check for child frames by name before built-in properties to
574 // match Mozilla. This does not match IE, but some sites end up
575 // naming frames things that conflict with window properties that
576 // are in Moz but not IE. Since we have some of these, we have to do
578 if (impl()->frame()->tree()->child(propertyName)) {
579 slot.setCustom(this, childFrameGetter);
583 const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName);
585 if (entry->attributes & Function) {
586 if (entry->functionValue == windowProtoFuncShowModalDialog) {
587 if (!canShowModalDialog(impl()->frame()))
590 if (allowsAccessFrom(exec))
591 slot.setStaticEntry(this, entry, staticFunctionGetter);
595 slot.setStaticEntry(this, entry, staticValueGetter<JSDOMWindowBase>);
599 // Do prototype lookup early so that functions and attributes in the prototype can have
600 // precedence over the index and name getters.
601 JSValue* proto = prototype();
602 if (proto->isObject()) {
603 if (static_cast<JSObject*>(proto)->getPropertySlot(exec, propertyName, slot)) {
604 if (!allowsAccessFrom(exec))
610 // FIXME: Search the whole frame hierachy somewhere around here.
611 // We need to test the correct priority order.
613 // allow window[1] or parent[1] etc. (#56983)
615 unsigned i = propertyName.toArrayIndex(&ok);
616 if (ok && i < impl()->frame()->tree()->childCount()) {
617 slot.setCustomIndex(this, i, indexGetter);
621 if (!allowsAccessFrom(exec)) {
626 // Allow shortcuts like 'Image1' instead of document.images.Image1
627 Document* document = impl()->frame()->document();
628 if (document && document->isHTMLDocument()) {
629 AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
630 if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
631 slot.setCustom(this, namedItemGetter);
636 return Base::getOwnPropertySlot(exec, propertyName, slot);
639 void JSDOMWindowBase::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
641 const HashEntry* entry = JSDOMWindowBaseTable.entry(exec, propertyName);
643 if (entry->attributes & Function) {
644 if (allowsAccessFrom(exec))
645 Base::put(exec, propertyName, value, slot);
648 if (entry->attributes & ReadOnly)
651 switch (entry->integerValue) {
653 if (allowsAccessFrom(exec))
654 setListener(exec, abortEvent, value);
657 if (allowsAccessFrom(exec))
658 setListener(exec, blurEvent, value);
661 if (allowsAccessFrom(exec))
662 setListener(exec, changeEvent, value);
665 if (allowsAccessFrom(exec))
666 setListener(exec, clickEvent, value);
669 if (allowsAccessFrom(exec))
670 setListener(exec, dblclickEvent, value);
673 if (allowsAccessFrom(exec))
674 setListener(exec, errorEvent, value);
677 if (allowsAccessFrom(exec))
678 setListener(exec, focusEvent, value);
681 if (allowsAccessFrom(exec))
682 setListener(exec, keydownEvent, value);
685 if (allowsAccessFrom(exec))
686 setListener(exec, keypressEvent, value);
689 if (allowsAccessFrom(exec))
690 setListener(exec, keyupEvent, value);
693 if (allowsAccessFrom(exec))
694 setListener(exec, loadEvent, value);
697 if (allowsAccessFrom(exec))
698 setListener(exec, mousedownEvent, value);
701 if (allowsAccessFrom(exec))
702 setListener(exec, mousemoveEvent, value);
705 if (allowsAccessFrom(exec))
706 setListener(exec, mouseoutEvent, value);
709 if (allowsAccessFrom(exec))
710 setListener(exec, mouseoverEvent, value);
713 if (allowsAccessFrom(exec))
714 setListener(exec, mouseupEvent, value);
716 case OnWindowMouseWheel:
717 if (allowsAccessFrom(exec))
718 setListener(exec, mousewheelEvent, value);
721 if (allowsAccessFrom(exec))
722 setListener(exec, resetEvent, value);
725 if (allowsAccessFrom(exec))
726 setListener(exec, resizeEvent, value);
729 if (allowsAccessFrom(exec))
730 setListener(exec, scrollEvent, value);
733 if (allowsAccessFrom(exec))
734 setListener(exec, searchEvent, value);
737 if (allowsAccessFrom(exec))
738 setListener(exec, selectEvent, value);
741 if (allowsAccessFrom(exec))
742 setListener(exec, submitEvent, value);
745 if (allowsAccessFrom(exec))
746 setListener(exec, beforeunloadEvent, value);
749 if (allowsAccessFrom(exec))
750 setListener(exec, unloadEvent, value);
752 case OnWebKitAnimationStart:
753 if (allowsAccessFrom(exec))
754 setListener(exec, webkitAnimationStartEvent, value);
756 case OnWebKitAnimationIteration:
757 if (allowsAccessFrom(exec))
758 setListener(exec, webkitAnimationIterationEvent, value);
760 case OnWebKitAnimationEnd:
761 if (allowsAccessFrom(exec))
762 setListener(exec, webkitAnimationEndEvent, value);
764 case OnWebKitTransitionEnd:
765 if (allowsAccessFrom(exec))
766 setListener(exec, webkitTransitionEndEvent, value);
773 if (allowsAccessFrom(exec))
774 Base::put(exec, propertyName, value, slot);
777 String JSDOMWindowBase::crossDomainAccessErrorMessage(const JSGlobalObject* other) const
779 KURL originURL = asJSDOMWindow(other)->impl()->url();
780 KURL targetURL = impl()->frame()->document()->url();
781 if (originURL.isNull() || targetURL.isNull())
784 // FIXME: this error message should contain more specifics of why the same origin check has failed.
785 return String::format("Unsafe JavaScript attempt to access frame with URL %s from frame with URL %s. Domains, protocols and ports must match.\n",
786 targetURL.string().utf8().data(), originURL.string().utf8().data());
789 void JSDOMWindowBase::printErrorMessage(const String& message) const
791 if (message.isEmpty())
794 Frame* frame = impl()->frame();
798 Settings* settings = frame->settings();
802 if (settings->privateBrowsingEnabled())
805 impl()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
808 ExecState* JSDOMWindowBase::globalExec()
810 // We need to make sure that any script execution happening in this
811 // frame does not destroy it
812 ASSERT(impl()->frame());
813 impl()->frame()->keepAlive();
814 return Base::globalExec();
817 bool JSDOMWindowBase::shouldInterruptScript() const
819 ASSERT(impl()->frame());
820 Page* page = impl()->frame()->page();
822 // See <rdar://problem/5479443>. We don't think that page can ever be NULL
823 // in this case, but if it is, we've gotten into a state where we may have
824 // hung the UI, with no way to ask the client whether to cancel execution.
825 // For now, our solution is just to cancel execution no matter what,
826 // ensuring that we never hang. We might want to consider other solutions
827 // if we discover problems with this one.
832 return page->chrome()->shouldInterruptJavaScript();
835 void JSDOMWindowBase::setListener(ExecState* exec, const AtomicString& eventType, JSValue* func)
837 ASSERT(impl()->frame());
838 Document* doc = impl()->frame()->document();
842 doc->setHTMLWindowEventListener(eventType, findOrCreateJSEventListener(exec, func, true));
845 JSValue* JSDOMWindowBase::getListener(ExecState* exec, const AtomicString& eventType) const
847 ASSERT(impl()->frame());
848 Document* doc = impl()->frame()->document();
850 return jsUndefined();
852 EventListener* listener = doc->getHTMLWindowEventListener(eventType);
853 if (listener && static_cast<JSEventListener*>(listener)->listenerObj())
854 return static_cast<JSEventListener*>(listener)->listenerObj();
858 JSEventListener* JSDOMWindowBase::findJSEventListener(JSValue* val, bool html)
860 if (!val->isObject())
862 JSObject* object = static_cast<JSObject*>(val);
863 ListenersMap& listeners = html ? d()->jsHTMLEventListeners : d()->jsEventListeners;
864 return listeners.get(object);
867 PassRefPtr<JSEventListener> JSDOMWindowBase::findOrCreateJSEventListener(ExecState* exec, JSValue* val, bool html)
869 JSEventListener* listener = findJSEventListener(val, html);
873 if (!val->isObject())
875 JSObject* object = static_cast<JSObject*>(val);
877 // Note that the JSEventListener constructor adds it to our jsEventListeners list
878 return JSEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
881 JSUnprotectedEventListener* JSDOMWindowBase::findJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
883 if (!val->isObject())
885 JSObject* object = static_cast<JSObject*>(val);
886 UnprotectedListenersMap& listeners = html ? d()->jsUnprotectedHTMLEventListeners : d()->jsUnprotectedEventListeners;
887 return listeners.get(object);
890 PassRefPtr<JSUnprotectedEventListener> JSDOMWindowBase::findOrCreateJSUnprotectedEventListener(ExecState* exec, JSValue* val, bool html)
892 JSUnprotectedEventListener* listener = findJSUnprotectedEventListener(exec, val, html);
895 if (!val->isObject())
897 JSObject* object = static_cast<JSObject*>(val);
899 // The JSUnprotectedEventListener constructor adds it to our jsUnprotectedEventListeners map.
900 return JSUnprotectedEventListener::create(object, static_cast<JSDOMWindow*>(this), html).get();
903 void JSDOMWindowBase::clearHelperObjectProperties()
908 void JSDOMWindowBase::clear()
912 if (d()->returnValueSlot && !*d()->returnValueSlot)
913 *d()->returnValueSlot = getDirect(Identifier(globalExec(), "returnValue"));
916 clearHelperObjectProperties();
919 void JSDOMWindowBase::setCurrentEvent(Event* evt)
924 Event* JSDOMWindowBase::currentEvent()
929 JSObject* JSDOMWindowBase::toThisObject(ExecState*) const
934 JSDOMWindowShell* JSDOMWindowBase::shell() const
939 JSGlobalData* JSDOMWindowBase::commonJSGlobalData()
941 static JSGlobalData* globalData = JSGlobalData::create().releaseRef();
945 JSValue* windowProtoFuncAToB(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
947 JSDOMWindow* window = toJSDOMWindow(thisValue);
949 return throwError(exec, TypeError);
950 if (!window->allowsAccessFrom(exec))
951 return jsUndefined();
954 return throwError(exec, SyntaxError, "Not enough arguments");
956 JSValue* v = args.at(exec, 0);
958 return jsEmptyString(exec);
960 UString s = v->toString(exec);
962 setDOMException(exec, INVALID_CHARACTER_ERR);
963 return jsUndefined();
966 Vector<char> in(s.size());
967 for (int i = 0; i < s.size(); ++i)
968 in[i] = static_cast<char>(s.data()[i]);
971 if (!base64Decode(in, out))
972 return throwError(exec, GeneralError, "Cannot decode base64");
974 return jsString(exec, String(out.data(), out.size()));
977 JSValue* windowProtoFuncBToA(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
979 JSDOMWindow* window = toJSDOMWindow(thisValue);
981 return throwError(exec, TypeError);
982 if (!window->allowsAccessFrom(exec))
983 return jsUndefined();
986 return throwError(exec, SyntaxError, "Not enough arguments");
988 JSValue* v = args.at(exec, 0);
990 return jsEmptyString(exec);
992 UString s = v->toString(exec);
994 setDOMException(exec, INVALID_CHARACTER_ERR);
995 return jsUndefined();
998 Vector<char> in(s.size());
999 for (int i = 0; i < s.size(); ++i)
1000 in[i] = static_cast<char>(s.data()[i]);
1003 base64Encode(in, out);
1005 return jsString(exec, String(out.data(), out.size()));
1008 JSValue* windowProtoFuncOpen(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1010 JSDOMWindow* window = toJSDOMWindow(thisValue);
1012 return throwError(exec, TypeError);
1013 if (!window->allowsAccessFrom(exec))
1014 return jsUndefined();
1016 Frame* frame = window->impl()->frame();
1018 return jsUndefined();
1019 Frame* activeFrame = asJSDOMWindow(exec->dynamicGlobalObject())->impl()->frame();
1021 return jsUndefined();
1023 Page* page = frame->page();
1025 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0));
1026 AtomicString frameName = args.at(exec, 1)->isUndefinedOrNull() ? "_blank" : AtomicString(args.at(exec, 1)->toString(exec));
1028 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
1029 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
1030 if (!allowPopUp(exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
1031 return jsUndefined();
1033 // Get the target frame for the special cases of _top and _parent. In those
1034 // cases, we can schedule a location change right now and return early.
1035 bool topOrParent = false;
1036 if (frameName == "_top") {
1037 frame = frame->tree()->top();
1039 } else if (frameName == "_parent") {
1040 if (Frame* parent = frame->tree()->parent())
1045 if (!activeFrame->loader()->shouldAllowNavigation(frame))
1046 return jsUndefined();
1048 String completedURL;
1049 if (!urlString.isEmpty())
1050 completedURL = activeFrame->document()->completeURL(urlString).string();
1052 const JSDOMWindow* targetedWindow = toJSDOMWindow(frame);
1053 if (!completedURL.isEmpty() && (!protocolIs(completedURL, "javascript") || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
1054 bool userGesture = activeFrame->script()->processingUserGesture();
1055 frame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
1057 return toJS(exec, frame->domWindow());
1060 // In the case of a named frame or a new window, we'll use the createWindow() helper
1061 WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1062 FloatRect windowRect(windowFeatures.x, windowFeatures.y, windowFeatures.width, windowFeatures.height);
1063 DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
1065 windowFeatures.x = windowRect.x();
1066 windowFeatures.y = windowRect.y();
1067 windowFeatures.height = windowRect.height();
1068 windowFeatures.width = windowRect.width();
1070 frame = createWindow(exec, frame, urlString, frameName, windowFeatures, 0);
1073 return jsUndefined();
1075 return toJS(exec, frame->domWindow()); // global object
1078 static JSValue* setTimeoutOrInterval(ExecState* exec, JSValue* thisValue, const ArgList& args, bool timeout)
1080 JSDOMWindow* window = toJSDOMWindow(thisValue);
1082 return throwError(exec, TypeError);
1083 if (!window->allowsAccessFrom(exec))
1084 return jsUndefined();
1086 JSValue* v = args.at(exec, 0);
1087 int delay = args.at(exec, 1)->toInt32(exec);
1089 return jsNumber(exec, window->installTimeout(static_cast<JSString*>(v)->value(), delay, timeout));
1091 if (v->getCallData(callData) == CallTypeNone)
1092 return jsUndefined();
1094 args.getSlice(2, argsTail);
1095 return jsNumber(exec, window->installTimeout(exec, v, argsTail, delay, timeout));
1098 JSValue* windowProtoFuncClearTimeoutOrInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1100 JSDOMWindow* window = toJSDOMWindow(thisValue);
1102 return throwError(exec, TypeError);
1103 if (!window->allowsAccessFrom(exec))
1104 return jsUndefined();
1106 window->clearTimeout(args.at(exec, 0)->toInt32(exec));
1107 return jsUndefined();
1110 JSValue* windowProtoFuncSetTimeout(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1112 return setTimeoutOrInterval(exec, thisValue, args, true);
1115 JSValue* windowProtoFuncSetInterval(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1117 return setTimeoutOrInterval(exec, thisValue, args, false);
1120 JSValue* windowProtoFuncAddEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1122 JSDOMWindow* window = toJSDOMWindow(thisValue);
1124 return throwError(exec, TypeError);
1125 if (!window->allowsAccessFrom(exec))
1126 return jsUndefined();
1128 Frame* frame = window->impl()->frame();
1130 return jsUndefined();
1132 if (RefPtr<JSEventListener> listener = window->findOrCreateJSEventListener(exec, args.at(exec, 1))) {
1133 if (Document* doc = frame->document())
1134 doc->addWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener.release(), args.at(exec, 2)->toBoolean(exec));
1137 return jsUndefined();
1140 JSValue* windowProtoFuncRemoveEventListener(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1142 JSDOMWindow* window = toJSDOMWindow(thisValue);
1144 return throwError(exec, TypeError);
1145 if (!window->allowsAccessFrom(exec))
1146 return jsUndefined();
1148 Frame* frame = window->impl()->frame();
1150 return jsUndefined();
1152 if (JSEventListener* listener = window->findJSEventListener(args.at(exec, 1))) {
1153 if (Document* doc = frame->document())
1154 doc->removeWindowEventListener(AtomicString(args.at(exec, 0)->toString(exec)), listener, args.at(exec, 2)->toBoolean(exec));
1157 return jsUndefined();
1160 JSValue* windowProtoFuncShowModalDialog(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
1162 JSDOMWindow* window = toJSDOMWindow(thisValue);
1164 return throwError(exec, TypeError);
1165 if (!window->allowsAccessFrom(exec))
1166 return jsUndefined();
1168 Frame* frame = window->impl()->frame();
1170 return jsUndefined();
1172 return showModalDialog(exec, frame, valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)), args.at(exec, 1), valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 2)));
1175 JSValue* windowProtoFuncNotImplemented(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
1177 if (!toJSDOMWindow(thisValue))
1178 return throwError(exec, TypeError);
1179 return jsUndefined();
1182 void JSDOMWindowBase::setReturnValueSlot(JSValue** slot)
1184 d()->returnValueSlot = slot;
1187 ////////////////////// timeouts ////////////////////////
1189 void JSDOMWindowBase::clearAllTimeouts()
1191 deleteAllValues(d()->timeouts);
1192 d()->timeouts.clear();
1195 int JSDOMWindowBase::installTimeout(ScheduledAction* a, int t, bool singleShot)
1197 int timeoutId = ++lastUsedTimeoutId;
1199 // avoid wraparound going negative on us
1203 int nestLevel = timerNestingLevel + 1;
1204 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, nestLevel, this, a);
1205 ASSERT(!d()->timeouts.get(timeoutId));
1206 d()->timeouts.set(timeoutId, timer);
1207 // Use a minimum interval of 10 ms to match other browsers, but only once we've
1208 // nested enough to notice that we're repeating.
1209 // Faster timers might be "better", but they're incompatible.
1210 double interval = max(0.001, t * 0.001);
1211 if (interval < cMinimumTimerInterval && nestLevel >= cMaxTimerNestingLevel)
1212 interval = cMinimumTimerInterval;
1214 timer->startOneShot(interval);
1216 timer->startRepeating(interval);
1220 int JSDOMWindowBase::installTimeout(const UString& handler, int t, bool singleShot)
1222 return installTimeout(new ScheduledAction(handler), t, singleShot);
1225 int JSDOMWindowBase::installTimeout(ExecState* exec, JSValue* func, const ArgList& args, int t, bool singleShot)
1227 return installTimeout(new ScheduledAction(exec, func, args), t, singleShot);
1230 void JSDOMWindowBase::pauseTimeouts(OwnPtr<PausedTimeouts>& result)
1232 size_t timeoutsCount = d()->timeouts.size();
1233 if (!timeoutsCount) {
1238 PausedTimeout* t = new PausedTimeout[timeoutsCount];
1239 result.set(new PausedTimeouts(t, timeoutsCount));
1241 JSDOMWindowBaseData::TimeoutsMap::iterator it = d()->timeouts.begin();
1242 for (size_t i = 0; i != timeoutsCount; ++i, ++it) {
1243 int timeoutId = it->first;
1244 DOMWindowTimer* timer = it->second;
1245 t[i].timeoutId = timeoutId;
1246 t[i].nestingLevel = timer->nestingLevel();
1247 t[i].nextFireInterval = timer->nextFireInterval();
1248 t[i].repeatInterval = timer->repeatInterval();
1249 t[i].action = timer->takeAction();
1251 ASSERT(it == d()->timeouts.end());
1253 deleteAllValues(d()->timeouts);
1254 d()->timeouts.clear();
1257 void JSDOMWindowBase::resumeTimeouts(OwnPtr<PausedTimeouts>& timeouts)
1261 size_t count = timeouts->numTimeouts();
1262 PausedTimeout* array = timeouts->takeTimeouts();
1263 for (size_t i = 0; i != count; ++i) {
1264 int timeoutId = array[i].timeoutId;
1265 DOMWindowTimer* timer = new DOMWindowTimer(timeoutId, array[i].nestingLevel, this, array[i].action);
1266 d()->timeouts.set(timeoutId, timer);
1267 timer->start(array[i].nextFireInterval, array[i].repeatInterval);
1273 void JSDOMWindowBase::clearTimeout(int timeoutId, bool delAction)
1275 // timeout IDs have to be positive, and 0 and -1 are unsafe to
1276 // even look up since they are the empty and deleted value
1281 delete d()->timeouts.take(timeoutId);
1284 void JSDOMWindowBase::timerFired(DOMWindowTimer* timer)
1286 // Simple case for non-one-shot timers.
1287 if (timer->isActive()) {
1288 int timeoutId = timer->timeoutId();
1290 timer->action()->execute(shell());
1291 // The DOMWindowTimer object may have been deleted or replaced during execution,
1292 // so we re-fetch it.
1293 timer = d()->timeouts.get(timeoutId);
1297 if (timer->repeatInterval() && timer->repeatInterval() < cMinimumTimerInterval) {
1298 timer->setNestingLevel(timer->nestingLevel() + 1);
1299 if (timer->nestingLevel() >= cMaxTimerNestingLevel)
1300 timer->augmentRepeatInterval(cMinimumTimerInterval - timer->repeatInterval());
1305 // Delete timer before executing the action for one-shot timers.
1306 ScheduledAction* action = timer->takeAction();
1307 d()->timeouts.remove(timer->timeoutId());
1309 action->execute(shell());
1315 void JSDOMWindowBase::disconnectFrame()
1320 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsEventListeners()
1322 return d()->jsEventListeners;
1325 JSDOMWindowBase::ListenersMap& JSDOMWindowBase::jsHTMLEventListeners()
1327 return d()->jsHTMLEventListeners;
1330 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedEventListeners()
1332 return d()->jsUnprotectedEventListeners;
1335 JSDOMWindowBase::UnprotectedListenersMap& JSDOMWindowBase::jsUnprotectedHTMLEventListeners()
1337 return d()->jsUnprotectedHTMLEventListeners;
1340 void DOMWindowTimer::fired()
1342 timerNestingLevel = m_nestingLevel;
1343 m_object->timerFired(this);
1344 timerNestingLevel = 0;
1347 JSValue* toJS(ExecState*, DOMWindow* domWindow)
1351 Frame* frame = domWindow->frame();
1354 return frame->script()->windowShell();
1357 JSDOMWindow* toJSDOMWindow(Frame* frame)
1361 return frame->script()->windowShell()->window();
1364 JSDOMWindow* toJSDOMWindow(JSValue* value)
1366 if (!value->isObject())
1368 const ClassInfo* classInfo = static_cast<JSObject*>(value)->classInfo();
1369 if (classInfo == &JSDOMWindow::s_info)
1370 return static_cast<JSDOMWindow*>(value);
1371 if (classInfo == &JSDOMWindowShell::s_info)
1372 return static_cast<JSDOMWindowShell*>(value)->window();
1376 } // namespace WebCore