1/*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "JSDOMWindowCustom.h"
22
23#include "AtomicString.h"
24#include "Base64.h"
25#include "Chrome.h"
26#include "DOMWindow.h"
27#include "Document.h"
28#include "ExceptionCode.h"
29#include "FloatRect.h"
30#include "Frame.h"
31#include "FrameLoadRequest.h"
32#include "FrameLoader.h"
33#include "FrameTree.h"
34#include "FrameView.h"
35#include "HTMLCollection.h"
36#include "HTMLDocument.h"
37#include "History.h"
38#include "JSAudioConstructor.h"
39#include "JSDOMWindowShell.h"
40#include "JSEvent.h"
41#include "JSEventListener.h"
42#include "JSEventSourceConstructor.h"
43#include "JSHTMLCollection.h"
44#include "JSHistory.h"
45#include "JSImageConstructor.h"
46#include "JSLocation.h"
47#include "JSMessageChannelConstructor.h"
48#include "JSMessagePort.h"
49#include "JSMessagePortCustom.h"
50#include "JSOptionConstructor.h"
51
52#if ENABLE(SHARED_WORKERS)
53#include "JSSharedWorkerConstructor.h"
54#endif
55
56#if ENABLE(3D_CANVAS)
57#include "JSWebGLArrayBufferConstructor.h"
58#include "JSWebGLByteArrayConstructor.h"
59#include "JSWebGLUnsignedByteArrayConstructor.h"
60#include "JSWebGLIntArrayConstructor.h"
61#include "JSWebGLUnsignedIntArrayConstructor.h"
62#include "JSWebGLShortArrayConstructor.h"
63#include "JSWebGLUnsignedShortArrayConstructor.h"
64#include "JSWebGLFloatArrayConstructor.h"
65#endif
66#include "JSWebKitCSSMatrixConstructor.h"
67#include "JSWebKitPointConstructor.h"
68#if ENABLE(WEB_SOCKETS)
69#include "JSWebSocketConstructor.h"
70#endif
71#include "JSWorkerConstructor.h"
72#include "JSXMLHttpRequestConstructor.h"
73#include "JSXSLTProcessorConstructor.h"
74#include "Location.h"
75#include "MediaPlayer.h"
76#include "MessagePort.h"
77#include "NotificationCenter.h"
78#include "Page.h"
79#include "PlatformScreen.h"
80#include "RegisteredEventListener.h"
81#include "ScheduledAction.h"
82#include "ScriptController.h"
83#include "SerializedScriptValue.h"
84#include "Settings.h"
85#include "SharedWorkerRepository.h"
86#include "WindowFeatures.h"
87#include <runtime/Error.h>
88#include <runtime/JSFunction.h>
89#include <runtime/JSObject.h>
90#include <runtime/PrototypeFunction.h>
91
92using namespace JSC;
93
94namespace WebCore {
95
96void JSDOMWindow::markChildren(MarkStack& markStack)
97{
98    Base::markChildren(markStack);
99
100    impl()->markJSEventListeners(markStack);
101
102    JSGlobalData& globalData = *Heap::heap(this)->globalData();
103
104    markDOMObjectWrapper(markStack, globalData, impl()->optionalConsole());
105    markDOMObjectWrapper(markStack, globalData, impl()->optionalHistory());
106    markDOMObjectWrapper(markStack, globalData, impl()->optionalLocationbar());
107    markDOMObjectWrapper(markStack, globalData, impl()->optionalMenubar());
108    markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator());
109    markDOMObjectWrapper(markStack, globalData, impl()->optionalPersonalbar());
110    markDOMObjectWrapper(markStack, globalData, impl()->optionalScreen());
111    markDOMObjectWrapper(markStack, globalData, impl()->optionalScrollbars());
112    markDOMObjectWrapper(markStack, globalData, impl()->optionalSelection());
113    markDOMObjectWrapper(markStack, globalData, impl()->optionalStatusbar());
114    markDOMObjectWrapper(markStack, globalData, impl()->optionalToolbar());
115    markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation());
116    markDOMObjectWrapper(markStack, globalData, impl()->optionalMedia());
117#if ENABLE(DOM_STORAGE)
118    markDOMObjectWrapper(markStack, globalData, impl()->optionalSessionStorage());
119    markDOMObjectWrapper(markStack, globalData, impl()->optionalLocalStorage());
120#endif
121#if ENABLE(OFFLINE_WEB_APPLICATIONS)
122    markDOMObjectWrapper(markStack, globalData, impl()->optionalApplicationCache());
123#endif
124}
125
126template<NativeFunction nativeFunction, int length>
127JSValue nonCachingStaticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&)
128{
129    return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), length, propertyName, nativeFunction);
130}
131
132static JSValue childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
133{
134    return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow());
135}
136
137static JSValue indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
138{
139    return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(slot.index())->domWindow());
140}
141
142static JSValue namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
143{
144    JSDOMWindowBase* thisObj = static_cast<JSDOMWindow*>(asObject(slot.slotBase()));
145    Document* document = thisObj->impl()->frame()->document();
146
147    ASSERT(thisObj->allowsAccessFrom(exec));
148    ASSERT(document);
149    ASSERT(document->isHTMLDocument());
150
151    RefPtr<HTMLCollection> collection = document->windowNamedItems(propertyName);
152    if (collection->length() == 1)
153        return toJS(exec, collection->firstItem());
154    return toJS(exec, collection.get());
155}
156
157bool JSDOMWindow::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
158{
159    // When accessing a Window cross-domain, functions are always the native built-in ones, and they
160    // are not affected by properties changed on the Window or anything in its prototype chain.
161    // This is consistent with the behavior of Firefox.
162
163    const HashEntry* entry;
164
165    // We don't want any properties other than "close" and "closed" on a closed window.
166    if (!impl()->frame()) {
167        // The following code is safe for cross-domain and same domain use.
168        // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
169        entry = s_info.propHashTable(exec)->entry(exec, propertyName);
170        if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) {
171            slot.setCustom(this, entry->propertyGetter());
172            return true;
173        }
174        entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
175        if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) {
176            slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
177            return true;
178        }
179
180        // FIXME: We should have a message here that explains why the property access/function call was
181        // not allowed.
182        slot.setUndefined();
183        return true;
184    }
185
186    // We need to check for cross-domain access here without printing the generic warning message
187    // because we always allow access to some function, just different ones depending whether access
188    // is allowed.
189    String errorMessage;
190    bool allowsAccess = allowsAccessFrom(exec, errorMessage);
191
192    // Look for overrides before looking at any of our own properties, but ignore overrides completely
193    // if this is cross-domain access.
194    if (allowsAccess && JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot))
195        return true;
196
197    // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the
198    // prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot.
199    // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of
200    // what prototype is actually set on this object.
201    entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
202    if (entry) {
203        if (entry->attributes() & Function) {
204            if (entry->function() == jsDOMWindowPrototypeFunctionBlur) {
205                if (!allowsAccess) {
206                    slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>);
207                    return true;
208                }
209            } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) {
210                if (!allowsAccess) {
211                    slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
212                    return true;
213                }
214            } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) {
215                if (!allowsAccess) {
216                    slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>);
217                    return true;
218                }
219            } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) {
220                if (!allowsAccess) {
221                    slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>);
222                    return true;
223                }
224            } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) {
225                if (!DOMWindow::canShowModalDialog(impl()->frame())) {
226                    slot.setUndefined();
227                    return true;
228                }
229            }
230        }
231    } else {
232        // Allow access to toString() cross-domain, but always Object.prototype.toString.
233        if (propertyName == exec->propertyNames().toString) {
234            if (!allowsAccess) {
235                slot.setCustom(this, objectToStringFunctionGetter);
236                return true;
237            }
238        }
239    }
240
241    entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName);
242    if (entry) {
243        slot.setCustom(this, entry->propertyGetter());
244        return true;
245    }
246
247    // Check for child frames by name before built-in properties to
248    // match Mozilla. This does not match IE, but some sites end up
249    // naming frames things that conflict with window properties that
250    // are in Moz but not IE. Since we have some of these, we have to do
251    // it the Moz way.
252    if (impl()->frame()->tree()->child(propertyName)) {
253        slot.setCustom(this, childFrameGetter);
254        return true;
255    }
256
257    // Do prototype lookup early so that functions and attributes in the prototype can have
258    // precedence over the index and name getters.
259    JSValue proto = prototype();
260    if (proto.isObject()) {
261        if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
262            if (!allowsAccess) {
263                printErrorMessage(errorMessage);
264                slot.setUndefined();
265            }
266            return true;
267        }
268    }
269
270    // FIXME: Search the whole frame hierarchy somewhere around here.
271    // We need to test the correct priority order.
272
273    // allow window[1] or parent[1] etc. (#56983)
274    bool ok;
275    unsigned i = propertyName.toArrayIndex(&ok);
276    if (ok && i < impl()->frame()->tree()->childCount()) {
277        slot.setCustomIndex(this, i, indexGetter);
278        return true;
279    }
280
281    if (!allowsAccess) {
282        printErrorMessage(errorMessage);
283        slot.setUndefined();
284        return true;
285    }
286
287    // Allow shortcuts like 'Image1' instead of document.images.Image1
288    Document* document = impl()->frame()->document();
289    if (document->isHTMLDocument()) {
290        AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
291        if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
292            slot.setCustom(this, namedItemGetter);
293            return true;
294        }
295    }
296
297    return Base::getOwnPropertySlot(exec, propertyName, slot);
298}
299
300bool JSDOMWindow::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
301{
302    // Never allow cross-domain getOwnPropertyDescriptor
303    if (!allowsAccessFrom(exec))
304        return false;
305
306    const HashEntry* entry;
307
308    // We don't want any properties other than "close" and "closed" on a closed window.
309    if (!impl()->frame()) {
310        // The following code is safe for cross-domain and same domain use.
311        // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
312        entry = s_info.propHashTable(exec)->entry(exec, propertyName);
313        if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) {
314            descriptor.setDescriptor(jsBoolean(true), ReadOnly | DontDelete | DontEnum);
315            return true;
316        }
317        entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
318        if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) {
319            PropertySlot slot;
320            slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
321            descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
322            return true;
323        }
324        descriptor.setUndefined();
325        return true;
326    }
327
328    entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName);
329    if (entry) {
330        PropertySlot slot;
331        slot.setCustom(this, entry->propertyGetter());
332        descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
333        return true;
334    }
335
336    // Check for child frames by name before built-in properties to
337    // match Mozilla. This does not match IE, but some sites end up
338    // naming frames things that conflict with window properties that
339    // are in Moz but not IE. Since we have some of these, we have to do
340    // it the Moz way.
341    if (impl()->frame()->tree()->child(propertyName)) {
342        PropertySlot slot;
343        slot.setCustom(this, childFrameGetter);
344        descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
345        return true;
346    }
347
348    bool ok;
349    unsigned i = propertyName.toArrayIndex(&ok);
350    if (ok && i < impl()->frame()->tree()->childCount()) {
351        PropertySlot slot;
352        slot.setCustomIndex(this, i, indexGetter);
353        descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
354        return true;
355    }
356
357    // Allow shortcuts like 'Image1' instead of document.images.Image1
358    Document* document = impl()->frame()->document();
359    if (document->isHTMLDocument()) {
360        AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
361        if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
362            PropertySlot slot;
363            slot.setCustom(this, namedItemGetter);
364            descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum);
365            return true;
366        }
367    }
368
369    return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor);
370}
371
372void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
373{
374    if (!impl()->frame())
375        return;
376
377    // Optimization: access JavaScript global variables directly before involving the DOM.
378    if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
379        if (allowsAccessFrom(exec))
380            JSGlobalObject::put(exec, propertyName, value, slot);
381        return;
382    }
383
384    if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this))
385        return;
386
387    if (allowsAccessFrom(exec))
388        Base::put(exec, propertyName, value, slot);
389}
390
391bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName)
392{
393    // Only allow deleting properties by frames in the same origin.
394    if (!allowsAccessFrom(exec))
395        return false;
396    return Base::deleteProperty(exec, propertyName);
397}
398
399void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
400{
401    // Only allow the window to enumerated by frames in the same origin.
402    if (!allowsAccessFrom(exec))
403        return;
404    Base::getPropertyNames(exec, propertyNames, mode);
405}
406
407void JSDOMWindow::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
408{
409    // Only allow the window to enumerated by frames in the same origin.
410    if (!allowsAccessFrom(exec))
411        return;
412    Base::getOwnPropertyNames(exec, propertyNames, mode);
413}
414
415void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
416{
417    // Only allow defining getters by frames in the same origin.
418    if (!allowsAccessFrom(exec))
419        return;
420
421    // Don't allow shadowing location using defineGetter.
422    if (propertyName == "location")
423        return;
424
425    Base::defineGetter(exec, propertyName, getterFunction, attributes);
426}
427
428void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
429{
430    // Only allow defining setters by frames in the same origin.
431    if (!allowsAccessFrom(exec))
432        return;
433    Base::defineSetter(exec, propertyName, setterFunction, attributes);
434}
435
436bool JSDOMWindow::defineOwnProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow)
437{
438    // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
439    if (!allowsAccessFrom(exec))
440        return false;
441    return Base::defineOwnProperty(exec, propertyName, descriptor, shouldThrow);
442}
443
444JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName)
445{
446    // Only allow looking-up getters by frames in the same origin.
447    if (!allowsAccessFrom(exec))
448        return jsUndefined();
449    return Base::lookupGetter(exec, propertyName);
450}
451
452JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName)
453{
454    // Only allow looking-up setters by frames in the same origin.
455    if (!allowsAccessFrom(exec))
456        return jsUndefined();
457    return Base::lookupSetter(exec, propertyName);
458}
459
460// Custom Attributes
461
462JSValue JSDOMWindow::history(ExecState* exec) const
463{
464    History* history = impl()->history();
465    if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, history))
466        return wrapper;
467
468    JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
469    JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history);
470    cacheDOMObjectWrapper(exec, history, jsHistory);
471    return jsHistory;
472}
473
474JSValue JSDOMWindow::location(ExecState* exec) const
475{
476    Location* location = impl()->location();
477    if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, location))
478        return wrapper;
479
480    JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
481    JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location);
482    cacheDOMObjectWrapper(exec, location, jsLocation);
483    return jsLocation;
484}
485
486void JSDOMWindow::setLocation(ExecState* exec, JSValue value)
487{
488    Frame* lexicalFrame = toLexicalFrame(exec);
489    if (!lexicalFrame)
490        return;
491
492#if ENABLE(DASHBOARD_SUPPORT)
493    // To avoid breaking old widgets, make "var location =" in a top-level frame create
494    // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
495    if (Settings* settings = lexicalFrame->settings()) {
496        if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) {
497            if (allowsAccessFrom(exec))
498                putDirect(Identifier(exec, "location"), value);
499            return;
500        }
501    }
502#endif
503
504    Frame* frame = impl()->frame();
505    ASSERT(frame);
506
507    KURL url = completeURL(exec, value.toString(exec));
508    if (url.isNull())
509        return;
510
511    if (!shouldAllowNavigation(exec, frame))
512        return;
513
514    if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) {
515        // We want a new history item if this JS was called via a user gesture
516        frame->redirectScheduler()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec));
517    }
518}
519
520JSValue JSDOMWindow::crypto(ExecState*) const
521{
522    return jsUndefined();
523}
524
525JSValue JSDOMWindow::event(ExecState* exec) const
526{
527    Event* event = currentEvent();
528    if (!event)
529        return jsUndefined();
530    return toJS(exec, event);
531}
532
533#if ENABLE(EVENTSOURCE)
534JSValue JSDOMWindow::eventSource(ExecState* exec) const
535{
536    return getDOMConstructor<JSEventSourceConstructor>(exec, this);
537}
538#endif
539
540JSValue JSDOMWindow::image(ExecState* exec) const
541{
542    return getDOMConstructor<JSImageConstructor>(exec, this);
543}
544
545JSValue JSDOMWindow::option(ExecState* exec) const
546{
547    return getDOMConstructor<JSOptionConstructor>(exec, this);
548}
549
550#if ENABLE(VIDEO)
551JSValue JSDOMWindow::audio(ExecState* exec) const
552{
553    if (!MediaPlayer::isAvailable())
554        return jsUndefined();
555    return getDOMConstructor<JSAudioConstructor>(exec, this);
556}
557#endif
558
559JSValue JSDOMWindow::webKitPoint(ExecState* exec) const
560{
561    return getDOMConstructor<JSWebKitPointConstructor>(exec, this);
562}
563
564JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const
565{
566    return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this);
567}
568
569#if ENABLE(3D_CANVAS)
570JSValue JSDOMWindow::webGLArrayBuffer(ExecState* exec) const
571{
572    return getDOMConstructor<JSWebGLArrayBufferConstructor>(exec, this);
573}
574
575JSValue JSDOMWindow::webGLByteArray(ExecState* exec) const
576{
577    return getDOMConstructor<JSWebGLByteArrayConstructor>(exec, this);
578}
579
580JSValue JSDOMWindow::webGLUnsignedByteArray(ExecState* exec) const
581{
582    return getDOMConstructor<JSWebGLUnsignedByteArrayConstructor>(exec, this);
583}
584
585JSValue JSDOMWindow::webGLIntArray(ExecState* exec) const
586{
587    return getDOMConstructor<JSWebGLIntArrayConstructor>(exec, this);
588}
589
590JSValue JSDOMWindow::webGLUnsignedIntArray(ExecState* exec) const
591{
592    return getDOMConstructor<JSWebGLUnsignedIntArrayConstructor>(exec, this);
593}
594
595JSValue JSDOMWindow::webGLShortArray(ExecState* exec) const
596{
597    return getDOMConstructor<JSWebGLShortArrayConstructor>(exec, this);
598}
599
600JSValue JSDOMWindow::webGLUnsignedShortArray(ExecState* exec) const
601{
602    return getDOMConstructor<JSWebGLUnsignedShortArrayConstructor>(exec, this);
603}
604
605JSValue JSDOMWindow::webGLFloatArray(ExecState* exec) const
606{
607    return getDOMConstructor<JSWebGLFloatArrayConstructor>(exec, this);
608}
609#endif
610
611JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const
612{
613    return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this);
614}
615
616#if ENABLE(XSLT)
617JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const
618{
619    return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this);
620}
621#endif
622
623#if ENABLE(CHANNEL_MESSAGING)
624JSValue JSDOMWindow::messageChannel(ExecState* exec) const
625{
626    return getDOMConstructor<JSMessageChannelConstructor>(exec, this);
627}
628#endif
629
630#if ENABLE(WORKERS)
631JSValue JSDOMWindow::worker(ExecState* exec) const
632{
633    return getDOMConstructor<JSWorkerConstructor>(exec, this);
634}
635#endif
636
637#if ENABLE(SHARED_WORKERS)
638JSValue JSDOMWindow::sharedWorker(ExecState* exec) const
639{
640    if (SharedWorkerRepository::isAvailable())
641        return getDOMConstructor<JSSharedWorkerConstructor>(exec, this);
642    return jsUndefined();
643}
644#endif
645
646#if ENABLE(WEB_SOCKETS)
647JSValue JSDOMWindow::webSocket(ExecState* exec) const
648{
649    Frame* frame = impl()->frame();
650    if (!frame)
651        return jsUndefined();
652    Settings* settings = frame->settings();
653    if (!settings)
654        return jsUndefined();
655    return getDOMConstructor<JSWebSocketConstructor>(exec, this);
656}
657#endif
658
659// Custom functions
660
661// Helper for window.open() and window.showModalDialog()
662static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame,
663                           Frame* openerFrame, const String& url, const String& frameName,
664                           const WindowFeatures& windowFeatures, JSValue dialogArgs)
665{
666    ASSERT(lexicalFrame);
667    ASSERT(dynamicFrame);
668
669    if (Document* lexicalDocument = lexicalFrame->document()) {
670        // Sandboxed iframes cannot open new auxiliary browsing contexts.
671        if (lexicalDocument->securityOrigin()->isSandboxed(SandboxNavigation))
672            return 0;
673    }
674
675    ResourceRequest request;
676
677    // For whatever reason, Firefox uses the dynamicGlobalObject to determine
678    // the outgoingReferrer.  We replicate that behavior here.
679    String referrer = dynamicFrame->loader()->outgoingReferrer();
680    request.setHTTPReferrer(referrer);
681    FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin());
682    FrameLoadRequest frameRequest(request, frameName);
683
684    // FIXME: It's much better for client API if a new window starts with a URL, here where we
685    // know what URL we are going to open. Unfortunately, this code passes the empty string
686    // for the URL, but there's a reason for that. Before loading we have to set up the opener,
687    // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
688    // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
689    // We'd have to resolve all those issues to pass the URL instead of "".
690
691    bool created;
692    // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
693    // is different from the opener frame, and the name references a frame relative to the opener frame, for example
694    // "_self" or "_parent".
695    Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
696    if (!newFrame)
697        return 0;
698
699    newFrame->loader()->setOpener(openerFrame);
700    newFrame->page()->setOpenedByDOM();
701
702    // FIXME: If a window is created from an isolated world, what are the consequences of this? 'dialogArguments' only appears back in the normal world?
703    JSDOMWindow* newWindow = toJSDOMWindow(newFrame, normalWorld(exec->globalData()));
704
705    if (dialogArgs)
706        newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
707
708    if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) {
709        KURL completedURL = url.isEmpty() ? KURL(ParsedURLString, "") : completeURL(exec, url);
710        bool userGesture = processingUserGesture(exec);
711
712        if (created)
713            newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture);
714        else if (!url.isEmpty())
715            newFrame->redirectScheduler()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
716    }
717
718    return newFrame;
719}
720
721static bool domWindowAllowPopUp(Frame* activeFrame, ExecState* exec)
722{
723    ASSERT(activeFrame);
724    if (activeFrame->script()->processingUserGesture(currentWorld(exec)))
725        return true;
726    return DOMWindow::allowPopUp(activeFrame);
727}
728
729JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args)
730{
731    String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
732    AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec));
733    WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2)));
734
735    Frame* frame = impl()->frame();
736    if (!frame)
737        return jsUndefined();
738    Frame* lexicalFrame = toLexicalFrame(exec);
739    if (!lexicalFrame)
740        return jsUndefined();
741    Frame* dynamicFrame = toDynamicFrame(exec);
742    if (!dynamicFrame)
743        return jsUndefined();
744
745    Page* page = frame->page();
746
747    // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
748    // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
749    if (!domWindowAllowPopUp(dynamicFrame, exec) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
750        return jsUndefined();
751
752    // Get the target frame for the special cases of _top and _parent.  In those
753    // cases, we can schedule a location change right now and return early.
754    bool topOrParent = false;
755    if (frameName == "_top") {
756        frame = frame->tree()->top();
757        topOrParent = true;
758    } else if (frameName == "_parent") {
759        if (Frame* parent = frame->tree()->parent())
760            frame = parent;
761        topOrParent = true;
762    }
763    if (topOrParent) {
764        String completedURL;
765        if (!urlString.isEmpty())
766            completedURL = completeURL(exec, urlString).string();
767
768        if (!shouldAllowNavigation(exec, frame))
769            return jsUndefined();
770
771        const JSDOMWindow* targetedWindow = toJSDOMWindow(frame, currentWorld(exec));
772        if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
773            bool userGesture = processingUserGesture(exec);
774
775            // For whatever reason, Firefox uses the dynamicGlobalObject to
776            // determine the outgoingReferrer.  We replicate that behavior
777            // here.
778            String referrer = dynamicFrame->loader()->outgoingReferrer();
779
780            frame->redirectScheduler()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
781        }
782        return toJS(exec, frame->domWindow());
783    }
784
785    // In the case of a named frame or a new window, we'll use the createWindow() helper
786    FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0,
787                         windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0);
788    DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
789
790    windowFeatures.x = windowRect.x();
791    windowFeatures.y = windowRect.y();
792    windowFeatures.height = windowRect.height();
793    windowFeatures.width = windowRect.width();
794
795    frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue());
796
797    if (!frame)
798        return jsUndefined();
799
800    return toJS(exec, frame->domWindow());
801}
802
803JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args)
804{
805    String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
806    JSValue dialogArgs = args.at(1);
807    String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2));
808
809    Frame* frame = impl()->frame();
810    if (!frame)
811        return jsUndefined();
812    Frame* lexicalFrame = toLexicalFrame(exec);
813    if (!lexicalFrame)
814        return jsUndefined();
815    Frame* dynamicFrame = toDynamicFrame(exec);
816    if (!dynamicFrame)
817        return jsUndefined();
818
819    if (!DOMWindow::canShowModalDialogNow(frame) || !domWindowAllowPopUp(dynamicFrame, exec))
820        return jsUndefined();
821
822    HashMap<String, String> features;
823    DOMWindow::parseModalDialogFeatures(featureArgs, features);
824
825    const bool trusted = false;
826
827    // The following features from Microsoft's documentation are not implemented:
828    // - default font settings
829    // - width, height, left, and top specified in units other than "px"
830    // - edge (sunken or raised, default is raised)
831    // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
832    // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
833    // - unadorned: trusted && boolFeature(features, "unadorned");
834
835    FloatRect screenRect = screenAvailableRect(frame->view());
836
837    WindowFeatures wargs;
838    wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
839    wargs.widthSet = true;
840    wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
841    wargs.heightSet = true;
842
843    wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
844    wargs.xSet = wargs.x > 0;
845    wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
846    wargs.ySet = wargs.y > 0;
847
848    if (WindowFeatures::boolFeature(features, "center", true)) {
849        if (!wargs.xSet) {
850            wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
851            wargs.xSet = true;
852        }
853        if (!wargs.ySet) {
854            wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
855            wargs.ySet = true;
856        }
857    }
858
859    wargs.dialog = true;
860    wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
861    wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
862    wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
863    wargs.menuBarVisible = false;
864    wargs.toolBarVisible = false;
865    wargs.locationBarVisible = false;
866    wargs.fullscreen = false;
867
868    Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs);
869    if (!dialogFrame)
870        return jsUndefined();
871
872    JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame, currentWorld(exec));
873    dialogFrame->page()->chrome()->runModal();
874
875    Identifier returnValue(exec, "returnValue");
876    if (dialogWindow->allowsAccessFromNoErrorMessage(exec)) {
877        PropertySlot slot;
878        // This is safe, we have already performed the origin security check and we are
879        // not interested in any of the DOM properties of the window.
880        if (dialogWindow->JSGlobalObject::getOwnPropertySlot(exec, returnValue, slot))
881            return slot.getValue(exec, returnValue);
882    }
883    return jsUndefined();
884}
885
886JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args)
887{
888    DOMWindow* window = impl();
889
890    DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl();
891    PassRefPtr<SerializedScriptValue> message = SerializedScriptValue::create(exec, args.at(0));
892
893    if (exec->hadException())
894        return jsUndefined();
895
896    MessagePortArray messagePorts;
897    if (args.size() > 2)
898        fillMessagePortArray(exec, args.at(1), messagePorts);
899    if (exec->hadException())
900        return jsUndefined();
901
902    String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2));
903    if (exec->hadException())
904        return jsUndefined();
905
906    ExceptionCode ec = 0;
907    window->postMessage(message, &messagePorts, targetOrigin, source, ec);
908    setDOMException(exec, ec);
909
910    return jsUndefined();
911}
912
913JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args)
914{
915    ScheduledAction* action = ScheduledAction::create(exec, args, currentWorld(exec));
916    if (exec->hadException())
917        return jsUndefined();
918    int delay = args.at(1).toInt32(exec);
919
920    ExceptionCode ec = 0;
921    int result = impl()->setTimeout(action, delay, ec);
922    setDOMException(exec, ec);
923
924    return jsNumber(exec, result);
925}
926
927JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args)
928{
929    ScheduledAction* action = ScheduledAction::create(exec, args, currentWorld(exec));
930    if (exec->hadException())
931        return jsUndefined();
932    int delay = args.at(1).toInt32(exec);
933
934    ExceptionCode ec = 0;
935    int result = impl()->setInterval(action, delay, ec);
936    setDOMException(exec, ec);
937
938    return jsNumber(exec, result);
939}
940
941JSValue JSDOMWindow::atob(ExecState* exec, const ArgList& args)
942{
943    if (args.size() < 1)
944        return throwError(exec, SyntaxError, "Not enough arguments");
945
946    JSValue v = args.at(0);
947    if (v.isNull())
948        return jsEmptyString(exec);
949
950    UString s = v.toString(exec);
951    if (!s.is8Bit()) {
952        setDOMException(exec, INVALID_CHARACTER_ERR);
953        return jsUndefined();
954    }
955
956    Vector<char> in(s.size());
957    for (int i = 0; i < s.size(); ++i)
958        in[i] = static_cast<char>(s.data()[i]);
959    Vector<char> out;
960
961    if (!base64Decode(in, out))
962        return throwError(exec, GeneralError, "Cannot decode base64");
963
964    return jsString(exec, String(out.data(), out.size()));
965}
966
967JSValue JSDOMWindow::btoa(ExecState* exec, const ArgList& args)
968{
969    if (args.size() < 1)
970        return throwError(exec, SyntaxError, "Not enough arguments");
971
972    JSValue v = args.at(0);
973    if (v.isNull())
974        return jsEmptyString(exec);
975
976    UString s = v.toString(exec);
977    if (!s.is8Bit()) {
978        setDOMException(exec, INVALID_CHARACTER_ERR);
979        return jsUndefined();
980    }
981
982    Vector<char> in(s.size());
983    for (int i = 0; i < s.size(); ++i)
984        in[i] = static_cast<char>(s.data()[i]);
985    Vector<char> out;
986
987    base64Encode(in, out);
988
989    return jsString(exec, String(out.data(), out.size()));
990}
991
992JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
993{
994    Frame* frame = impl()->frame();
995    if (!frame)
996        return jsUndefined();
997
998    JSValue listener = args.at(1);
999    if (!listener.isObject())
1000        return jsUndefined();
1001
1002    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
1003    return jsUndefined();
1004}
1005
1006JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
1007{
1008    Frame* frame = impl()->frame();
1009    if (!frame)
1010        return jsUndefined();
1011
1012    JSValue listener = args.at(1);
1013    if (!listener.isObject())
1014        return jsUndefined();
1015
1016    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
1017    return jsUndefined();
1018}
1019
1020DOMWindow* toDOMWindow(JSValue value)
1021{
1022    if (!value.isObject())
1023        return 0;
1024    JSObject* object = asObject(value);
1025    if (object->inherits(&JSDOMWindow::s_info))
1026        return static_cast<JSDOMWindow*>(object)->impl();
1027    if (object->inherits(&JSDOMWindowShell::s_info))
1028        return static_cast<JSDOMWindowShell*>(object)->impl();
1029    return 0;
1030}
1031
1032} // namespace WebCore
1033