/* | |
* Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Library General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Library General Public License for more details. | |
* | |
* You should have received a copy of the GNU Library General Public License | |
* along with this library; see the file COPYING.LIB. If not, write to | |
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
* Boston, MA 02110-1301, USA. | |
*/ | |
#include "config.h" | |
#include "JSDOMWindowCustom.h" | |
#include "AtomicString.h" | |
#include "Chrome.h" | |
#include "Database.h" | |
#include "DOMWindow.h" | |
#include "Document.h" | |
#include "ExceptionCode.h" | |
#include "FloatRect.h" | |
#include "Frame.h" | |
#include "FrameLoadRequest.h" | |
#include "FrameLoader.h" | |
#include "FrameTree.h" | |
#include "FrameView.h" | |
#include "HTMLCollection.h" | |
#include "HTMLDocument.h" | |
#include "History.h" | |
#include "JSAudioConstructor.h" | |
#include "JSDatabase.h" | |
#include "JSDatabaseCallback.h" | |
#include "JSDOMWindowShell.h" | |
#include "JSEvent.h" | |
#include "JSEventListener.h" | |
#include "JSEventSourceConstructor.h" | |
#include "JSHTMLCollection.h" | |
#include "JSHistory.h" | |
#include "JSImageConstructor.h" | |
#include "JSLocation.h" | |
#include "JSMessageChannelConstructor.h" | |
#include "JSMessagePort.h" | |
#include "JSMessagePortCustom.h" | |
#include "JSOptionConstructor.h" | |
#if ENABLE(SHARED_WORKERS) | |
#include "JSSharedWorkerConstructor.h" | |
#endif | |
#if ENABLE(3D_CANVAS) | |
#include "JSWebGLArrayBufferConstructor.h" | |
#include "JSWebGLByteArrayConstructor.h" | |
#include "JSWebGLUnsignedByteArrayConstructor.h" | |
#include "JSWebGLIntArrayConstructor.h" | |
#include "JSWebGLUnsignedIntArrayConstructor.h" | |
#include "JSWebGLShortArrayConstructor.h" | |
#include "JSWebGLUnsignedShortArrayConstructor.h" | |
#include "JSWebGLFloatArrayConstructor.h" | |
#endif | |
#include "JSWebKitCSSMatrixConstructor.h" | |
#include "JSWebKitPointConstructor.h" | |
#if ENABLE(WEB_SOCKETS) | |
#include "JSWebSocketConstructor.h" | |
#endif | |
#include "JSWorkerConstructor.h" | |
#include "JSXMLHttpRequestConstructor.h" | |
#include "JSXSLTProcessorConstructor.h" | |
#include "Location.h" | |
#include "MediaPlayer.h" | |
#include "MessagePort.h" | |
#include "NotificationCenter.h" | |
#include "Page.h" | |
#include "PlatformScreen.h" | |
#include "RegisteredEventListener.h" | |
#include "ScheduledAction.h" | |
#include "ScriptController.h" | |
#include "SerializedScriptValue.h" | |
#include "Settings.h" | |
#include "SharedWorkerRepository.h" | |
#include "WindowFeatures.h" | |
#include <runtime/Error.h> | |
#include <runtime/JSFunction.h> | |
#include <runtime/JSObject.h> | |
#include <runtime/PrototypeFunction.h> | |
using namespace JSC; | |
namespace WebCore { | |
void JSDOMWindow::markChildren(MarkStack& markStack) | |
{ | |
Base::markChildren(markStack); | |
impl()->markJSEventListeners(markStack); | |
JSGlobalData& globalData = *Heap::heap(this)->globalData(); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalConsole()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalHistory()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalLocationbar()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalMenubar()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalPersonalbar()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalScreen()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalScrollbars()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalSelection()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalStatusbar()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalToolbar()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalMedia()); | |
#if ENABLE(DOM_STORAGE) | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalSessionStorage()); | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalLocalStorage()); | |
#endif | |
#if ENABLE(OFFLINE_WEB_APPLICATIONS) | |
markDOMObjectWrapper(markStack, globalData, impl()->optionalApplicationCache()); | |
#endif | |
} | |
template<NativeFunction nativeFunction, int length> | |
JSValue nonCachingStaticFunctionGetter(ExecState* exec, JSValue, const Identifier& propertyName) | |
{ | |
return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), length, propertyName, nativeFunction); | |
} | |
static JSValue childFrameGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) | |
{ | |
return toJS(exec, static_cast<JSDOMWindow*>(asObject(slotBase))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow()); | |
} | |
static JSValue indexGetter(ExecState* exec, JSValue slotBase, unsigned index) | |
{ | |
return toJS(exec, static_cast<JSDOMWindow*>(asObject(slotBase))->impl()->frame()->tree()->child(index)->domWindow()); | |
} | |
static JSValue namedItemGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) | |
{ | |
JSDOMWindowBase* thisObj = static_cast<JSDOMWindow*>(asObject(slotBase)); | |
Document* document = thisObj->impl()->frame()->document(); | |
ASSERT(thisObj->allowsAccessFrom(exec)); | |
ASSERT(document); | |
ASSERT(document->isHTMLDocument()); | |
RefPtr<HTMLCollection> collection = document->windowNamedItems(propertyName); | |
if (collection->length() == 1) | |
return toJS(exec, collection->firstItem()); | |
return toJS(exec, collection.get()); | |
} | |
bool JSDOMWindow::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
{ | |
// When accessing a Window cross-domain, functions are always the native built-in ones, and they | |
// are not affected by properties changed on the Window or anything in its prototype chain. | |
// This is consistent with the behavior of Firefox. | |
const HashEntry* entry; | |
// We don't want any properties other than "close" and "closed" on a closed window. | |
if (!impl()->frame()) { | |
// The following code is safe for cross-domain and same domain use. | |
// It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). | |
entry = s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) { | |
slot.setCustom(this, entry->propertyGetter()); | |
return true; | |
} | |
entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); | |
return true; | |
} | |
// FIXME: We should have a message here that explains why the property access/function call was | |
// not allowed. | |
slot.setUndefined(); | |
return true; | |
} | |
// We need to check for cross-domain access here without printing the generic warning message | |
// because we always allow access to some function, just different ones depending whether access | |
// is allowed. | |
String errorMessage; | |
bool allowsAccess = allowsAccessFrom(exec, errorMessage); | |
// Look for overrides before looking at any of our own properties, but ignore overrides completely | |
// if this is cross-domain access. | |
if (allowsAccess && JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot)) | |
return true; | |
// We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the | |
// prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot. | |
// Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of | |
// what prototype is actually set on this object. | |
entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry) { | |
if (entry->attributes() & Function) { | |
if (entry->function() == jsDOMWindowPrototypeFunctionBlur) { | |
if (!allowsAccess) { | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>); | |
return true; | |
} | |
} else if (entry->function() == jsDOMWindowPrototypeFunctionClose) { | |
if (!allowsAccess) { | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); | |
return true; | |
} | |
} else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) { | |
if (!allowsAccess) { | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>); | |
return true; | |
} | |
} else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) { | |
if (!allowsAccess) { | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>); | |
return true; | |
} | |
} else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) { | |
if (!DOMWindow::canShowModalDialog(impl()->frame())) { | |
slot.setUndefined(); | |
return true; | |
} | |
} | |
} | |
} else { | |
// Allow access to toString() cross-domain, but always Object.prototype.toString. | |
if (propertyName == exec->propertyNames().toString) { | |
if (!allowsAccess) { | |
slot.setCustom(this, objectToStringFunctionGetter); | |
return true; | |
} | |
} | |
} | |
entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry) { | |
slot.setCustom(this, entry->propertyGetter()); | |
return true; | |
} | |
// Check for child frames by name before built-in properties to | |
// match Mozilla. This does not match IE, but some sites end up | |
// naming frames things that conflict with window properties that | |
// are in Moz but not IE. Since we have some of these, we have to do | |
// it the Moz way. | |
if (impl()->frame()->tree()->child(propertyName)) { | |
slot.setCustom(this, childFrameGetter); | |
return true; | |
} | |
// Do prototype lookup early so that functions and attributes in the prototype can have | |
// precedence over the index and name getters. | |
JSValue proto = prototype(); | |
if (proto.isObject()) { | |
if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { | |
if (!allowsAccess) { | |
printErrorMessage(errorMessage); | |
slot.setUndefined(); | |
} | |
return true; | |
} | |
} | |
// FIXME: Search the whole frame hierarchy somewhere around here. | |
// We need to test the correct priority order. | |
// allow window[1] or parent[1] etc. (#56983) | |
bool ok; | |
unsigned i = propertyName.toArrayIndex(&ok); | |
if (ok && i < impl()->frame()->tree()->childCount()) { | |
slot.setCustomIndex(this, i, indexGetter); | |
return true; | |
} | |
if (!allowsAccess) { | |
printErrorMessage(errorMessage); | |
slot.setUndefined(); | |
return true; | |
} | |
// Allow shortcuts like 'Image1' instead of document.images.Image1 | |
Document* document = impl()->frame()->document(); | |
if (document->isHTMLDocument()) { | |
AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName); | |
if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { | |
slot.setCustom(this, namedItemGetter); | |
return true; | |
} | |
} | |
return Base::getOwnPropertySlot(exec, propertyName, slot); | |
} | |
bool JSDOMWindow::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) | |
{ | |
// Never allow cross-domain getOwnPropertyDescriptor | |
if (!allowsAccessFrom(exec)) | |
return false; | |
const HashEntry* entry; | |
// We don't want any properties other than "close" and "closed" on a closed window. | |
if (!impl()->frame()) { | |
// The following code is safe for cross-domain and same domain use. | |
// It ignores any custom properties that might be set on the DOMWindow (including a custom prototype). | |
entry = s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) { | |
descriptor.setDescriptor(jsBoolean(true), ReadOnly | DontDelete | DontEnum); | |
return true; | |
} | |
entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { | |
PropertySlot slot; | |
slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); | |
descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); | |
return true; | |
} | |
descriptor.setUndefined(); | |
return true; | |
} | |
entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName); | |
if (entry) { | |
PropertySlot slot; | |
slot.setCustom(this, entry->propertyGetter()); | |
descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); | |
return true; | |
} | |
// Check for child frames by name before built-in properties to | |
// match Mozilla. This does not match IE, but some sites end up | |
// naming frames things that conflict with window properties that | |
// are in Moz but not IE. Since we have some of these, we have to do | |
// it the Moz way. | |
if (impl()->frame()->tree()->child(propertyName)) { | |
PropertySlot slot; | |
slot.setCustom(this, childFrameGetter); | |
descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); | |
return true; | |
} | |
bool ok; | |
unsigned i = propertyName.toArrayIndex(&ok); | |
if (ok && i < impl()->frame()->tree()->childCount()) { | |
PropertySlot slot; | |
slot.setCustomIndex(this, i, indexGetter); | |
descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); | |
return true; | |
} | |
// Allow shortcuts like 'Image1' instead of document.images.Image1 | |
Document* document = impl()->frame()->document(); | |
if (document->isHTMLDocument()) { | |
AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName); | |
if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) { | |
PropertySlot slot; | |
slot.setCustom(this, namedItemGetter); | |
descriptor.setDescriptor(slot.getValue(exec, propertyName), ReadOnly | DontDelete | DontEnum); | |
return true; | |
} | |
} | |
return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); | |
} | |
void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) | |
{ | |
if (!impl()->frame()) | |
return; | |
// Optimization: access JavaScript global variables directly before involving the DOM. | |
if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { | |
if (allowsAccessFrom(exec)) | |
JSGlobalObject::put(exec, propertyName, value, slot); | |
return; | |
} | |
if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this)) | |
return; | |
if (allowsAccessFrom(exec)) | |
Base::put(exec, propertyName, value, slot); | |
} | |
bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName) | |
{ | |
// Only allow deleting properties by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return false; | |
return Base::deleteProperty(exec, propertyName); | |
} | |
void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) | |
{ | |
// Only allow the window to enumerated by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return; | |
Base::getPropertyNames(exec, propertyNames, mode); | |
} | |
void JSDOMWindow::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) | |
{ | |
// Only allow the window to enumerated by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return; | |
Base::getOwnPropertyNames(exec, propertyNames, mode); | |
} | |
void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) | |
{ | |
// Only allow defining getters by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return; | |
// Don't allow shadowing location using defineGetter. | |
if (propertyName == "location") | |
return; | |
Base::defineGetter(exec, propertyName, getterFunction, attributes); | |
} | |
void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) | |
{ | |
// Only allow defining setters by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return; | |
Base::defineSetter(exec, propertyName, setterFunction, attributes); | |
} | |
bool JSDOMWindow::defineOwnProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor, bool shouldThrow) | |
{ | |
// Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced. | |
if (!allowsAccessFrom(exec)) | |
return false; | |
return Base::defineOwnProperty(exec, propertyName, descriptor, shouldThrow); | |
} | |
JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName) | |
{ | |
// Only allow looking-up getters by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return jsUndefined(); | |
return Base::lookupGetter(exec, propertyName); | |
} | |
JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName) | |
{ | |
// Only allow looking-up setters by frames in the same origin. | |
if (!allowsAccessFrom(exec)) | |
return jsUndefined(); | |
return Base::lookupSetter(exec, propertyName); | |
} | |
// Custom Attributes | |
JSValue JSDOMWindow::history(ExecState* exec) const | |
{ | |
History* history = impl()->history(); | |
if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, history)) | |
return wrapper; | |
JSDOMWindow* window = const_cast<JSDOMWindow*>(this); | |
JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history); | |
cacheDOMObjectWrapper(exec, history, jsHistory); | |
return jsHistory; | |
} | |
JSValue JSDOMWindow::location(ExecState* exec) const | |
{ | |
Location* location = impl()->location(); | |
if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec, location)) | |
return wrapper; | |
JSDOMWindow* window = const_cast<JSDOMWindow*>(this); | |
JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location); | |
cacheDOMObjectWrapper(exec, location, jsLocation); | |
return jsLocation; | |
} | |
void JSDOMWindow::setLocation(ExecState* exec, JSValue value) | |
{ | |
Frame* lexicalFrame = toLexicalFrame(exec); | |
if (!lexicalFrame) | |
return; | |
#if ENABLE(DASHBOARD_SUPPORT) | |
// To avoid breaking old widgets, make "var location =" in a top-level frame create | |
// a property named "location" instead of performing a navigation (<rdar://problem/5688039>). | |
if (Settings* settings = lexicalFrame->settings()) { | |
if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) { | |
if (allowsAccessFrom(exec)) | |
putDirect(Identifier(exec, "location"), value); | |
return; | |
} | |
} | |
#endif | |
Frame* frame = impl()->frame(); | |
ASSERT(frame); | |
KURL url = completeURL(exec, value.toString(exec)); | |
if (url.isNull()) | |
return; | |
if (!shouldAllowNavigation(exec, frame)) | |
return; | |
if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) { | |
// We want a new history item if this JS was called via a user gesture | |
frame->redirectScheduler()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec)); | |
} | |
} | |
JSValue JSDOMWindow::crypto(ExecState*) const | |
{ | |
return jsUndefined(); | |
} | |
JSValue JSDOMWindow::event(ExecState* exec) const | |
{ | |
Event* event = currentEvent(); | |
if (!event) | |
return jsUndefined(); | |
return toJS(exec, event); | |
} | |
#if ENABLE(EVENTSOURCE) | |
JSValue JSDOMWindow::eventSource(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSEventSourceConstructor>(exec, this); | |
} | |
#endif | |
JSValue JSDOMWindow::image(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSImageConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::option(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSOptionConstructor>(exec, this); | |
} | |
#if ENABLE(VIDEO) | |
JSValue JSDOMWindow::audio(ExecState* exec) const | |
{ | |
if (!MediaPlayer::isAvailable()) | |
return jsUndefined(); | |
return getDOMConstructor<JSAudioConstructor>(exec, this); | |
} | |
#endif | |
JSValue JSDOMWindow::webKitPoint(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebKitPointConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this); | |
} | |
#if ENABLE(3D_CANVAS) | |
JSValue JSDOMWindow::webGLArrayBuffer(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLArrayBufferConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLByteArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLByteArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLUnsignedByteArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLUnsignedByteArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLIntArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLIntArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLUnsignedIntArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLUnsignedIntArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLShortArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLShortArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLUnsignedShortArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLUnsignedShortArrayConstructor>(exec, this); | |
} | |
JSValue JSDOMWindow::webGLFloatArray(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWebGLFloatArrayConstructor>(exec, this); | |
} | |
#endif | |
JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this); | |
} | |
#if ENABLE(XSLT) | |
JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this); | |
} | |
#endif | |
#if ENABLE(CHANNEL_MESSAGING) | |
JSValue JSDOMWindow::messageChannel(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSMessageChannelConstructor>(exec, this); | |
} | |
#endif | |
#if ENABLE(WORKERS) | |
JSValue JSDOMWindow::worker(ExecState* exec) const | |
{ | |
return getDOMConstructor<JSWorkerConstructor>(exec, this); | |
} | |
#endif | |
#if ENABLE(SHARED_WORKERS) | |
JSValue JSDOMWindow::sharedWorker(ExecState* exec) const | |
{ | |
if (SharedWorkerRepository::isAvailable()) | |
return getDOMConstructor<JSSharedWorkerConstructor>(exec, this); | |
return jsUndefined(); | |
} | |
#endif | |
#if ENABLE(WEB_SOCKETS) | |
JSValue JSDOMWindow::webSocket(ExecState* exec) const | |
{ | |
Frame* frame = impl()->frame(); | |
if (!frame) | |
return jsUndefined(); | |
Settings* settings = frame->settings(); | |
if (!settings) | |
return jsUndefined(); | |
return getDOMConstructor<JSWebSocketConstructor>(exec, this); | |
} | |
#endif | |
// Custom functions | |
// Helper for window.open() and window.showModalDialog() | |
static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame, | |
Frame* openerFrame, const String& url, const String& frameName, | |
const WindowFeatures& windowFeatures, JSValue dialogArgs) | |
{ | |
ASSERT(lexicalFrame); | |
ASSERT(dynamicFrame); | |
if (Document* lexicalDocument = lexicalFrame->document()) { | |
// Sandboxed iframes cannot open new auxiliary browsing contexts. | |
if (lexicalDocument->securityOrigin()->isSandboxed(SandboxNavigation)) | |
return 0; | |
} | |
ResourceRequest request; | |
// For whatever reason, Firefox uses the dynamicGlobalObject to determine | |
// the outgoingReferrer. We replicate that behavior here. | |
String referrer = dynamicFrame->loader()->outgoingReferrer(); | |
request.setHTTPReferrer(referrer); | |
FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin()); | |
FrameLoadRequest frameRequest(request, frameName); | |
// FIXME: It's much better for client API if a new window starts with a URL, here where we | |
// know what URL we are going to open. Unfortunately, this code passes the empty string | |
// for the URL, but there's a reason for that. Before loading we have to set up the opener, | |
// openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently | |
// do an allowsAccessFrom call using the window we create, which can't be done before creating it. | |
// We'd have to resolve all those issues to pass the URL instead of "". | |
bool created; | |
// We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame | |
// is different from the opener frame, and the name references a frame relative to the opener frame, for example | |
// "_self" or "_parent". | |
Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created); | |
if (!newFrame) | |
return 0; | |
newFrame->loader()->setOpener(openerFrame); | |
newFrame->page()->setOpenedByDOM(); | |
// FIXME: If a window is created from an isolated world, what are the consequences of this? 'dialogArguments' only appears back in the normal world? | |
JSDOMWindow* newWindow = toJSDOMWindow(newFrame, normalWorld(exec->globalData())); | |
if (dialogArgs) | |
newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs); | |
if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) { | |
KURL completedURL = url.isEmpty() ? KURL(ParsedURLString, "") : completeURL(exec, url); | |
bool userGesture = processingUserGesture(exec); | |
if (created) | |
newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture); | |
else if (!url.isEmpty()) | |
newFrame->redirectScheduler()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture); | |
} | |
return newFrame; | |
} | |
static bool domWindowAllowPopUp(Frame* activeFrame, ExecState* exec) | |
{ | |
ASSERT(activeFrame); | |
if (activeFrame->script()->processingUserGesture(currentWorld(exec))) | |
return true; | |
return DOMWindow::allowPopUp(activeFrame); | |
} | |
JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args) | |
{ | |
String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0)); | |
AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec)); | |
WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2))); | |
Frame* frame = impl()->frame(); | |
if (!frame) | |
return jsUndefined(); | |
Frame* lexicalFrame = toLexicalFrame(exec); | |
if (!lexicalFrame) | |
return jsUndefined(); | |
Frame* dynamicFrame = toDynamicFrame(exec); | |
if (!dynamicFrame) | |
return jsUndefined(); | |
Page* page = frame->page(); | |
// Because FrameTree::find() returns true for empty strings, we must check for empty framenames. | |
// Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. | |
if (!domWindowAllowPopUp(dynamicFrame, exec) && (frameName.isEmpty() || !frame->tree()->find(frameName))) | |
return jsUndefined(); | |
// Get the target frame for the special cases of _top and _parent. In those | |
// cases, we can schedule a location change right now and return early. | |
bool topOrParent = false; | |
if (frameName == "_top") { | |
frame = frame->tree()->top(); | |
topOrParent = true; | |
} else if (frameName == "_parent") { | |
if (Frame* parent = frame->tree()->parent()) | |
frame = parent; | |
topOrParent = true; | |
} | |
if (topOrParent) { | |
String completedURL; | |
if (!urlString.isEmpty()) | |
completedURL = completeURL(exec, urlString).string(); | |
if (!shouldAllowNavigation(exec, frame)) | |
return jsUndefined(); | |
const JSDOMWindow* targetedWindow = toJSDOMWindow(frame, currentWorld(exec)); | |
if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) { | |
bool userGesture = processingUserGesture(exec); | |
// For whatever reason, Firefox uses the dynamicGlobalObject to | |
// determine the outgoingReferrer. We replicate that behavior | |
// here. | |
String referrer = dynamicFrame->loader()->outgoingReferrer(); | |
frame->redirectScheduler()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture); | |
} | |
return toJS(exec, frame->domWindow()); | |
} | |
// In the case of a named frame or a new window, we'll use the createWindow() helper | |
FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0, | |
windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0); | |
DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect); | |
windowFeatures.x = windowRect.x(); | |
windowFeatures.y = windowRect.y(); | |
windowFeatures.height = windowRect.height(); | |
windowFeatures.width = windowRect.width(); | |
frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue()); | |
if (!frame) | |
return jsUndefined(); | |
return toJS(exec, frame->domWindow()); | |
} | |
JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args) | |
{ | |
String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0)); | |
JSValue dialogArgs = args.at(1); | |
String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2)); | |
Frame* frame = impl()->frame(); | |
if (!frame) | |
return jsUndefined(); | |
Frame* lexicalFrame = toLexicalFrame(exec); | |
if (!lexicalFrame) | |
return jsUndefined(); | |
Frame* dynamicFrame = toDynamicFrame(exec); | |
if (!dynamicFrame) | |
return jsUndefined(); | |
if (!DOMWindow::canShowModalDialogNow(frame) || !domWindowAllowPopUp(dynamicFrame, exec)) | |
return jsUndefined(); | |
HashMap<String, String> features; | |
DOMWindow::parseModalDialogFeatures(featureArgs, features); | |
const bool trusted = false; | |
// The following features from Microsoft's documentation are not implemented: | |
// - default font settings | |
// - width, height, left, and top specified in units other than "px" | |
// - edge (sunken or raised, default is raised) | |
// - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print | |
// - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?) | |
// - unadorned: trusted && boolFeature(features, "unadorned"); | |
FloatRect screenRect = screenAvailableRect(frame->view()); | |
WindowFeatures wargs; | |
wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE | |
wargs.widthSet = true; | |
wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE | |
wargs.heightSet = true; | |
wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1); | |
wargs.xSet = wargs.x > 0; | |
wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1); | |
wargs.ySet = wargs.y > 0; | |
if (WindowFeatures::boolFeature(features, "center", true)) { | |
if (!wargs.xSet) { | |
wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2; | |
wargs.xSet = true; | |
} | |
if (!wargs.ySet) { | |
wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2; | |
wargs.ySet = true; | |
} | |
} | |
wargs.dialog = true; | |
wargs.resizable = WindowFeatures::boolFeature(features, "resizable"); | |
wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true); | |
wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted); | |
wargs.menuBarVisible = false; | |
wargs.toolBarVisible = false; | |
wargs.locationBarVisible = false; | |
wargs.fullscreen = false; | |
Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs); | |
if (!dialogFrame) | |
return jsUndefined(); | |
JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame, currentWorld(exec)); | |
dialogFrame->page()->chrome()->runModal(); | |
Identifier returnValue(exec, "returnValue"); | |
if (dialogWindow->allowsAccessFromNoErrorMessage(exec)) { | |
PropertySlot slot; | |
// This is safe, we have already performed the origin security check and we are | |
// not interested in any of the DOM properties of the window. | |
if (dialogWindow->JSGlobalObject::getOwnPropertySlot(exec, returnValue, slot)) | |
return slot.getValue(exec, returnValue); | |
} | |
return jsUndefined(); | |
} | |
JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args) | |
{ | |
DOMWindow* window = impl(); | |
DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl(); | |
PassRefPtr<SerializedScriptValue> message = SerializedScriptValue::create(exec, args.at(0)); | |
if (exec->hadException()) | |
return jsUndefined(); | |
MessagePortArray messagePorts; | |
if (args.size() > 2) | |
fillMessagePortArray(exec, args.at(1), messagePorts); | |
if (exec->hadException()) | |
return jsUndefined(); | |
String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2)); | |
if (exec->hadException()) | |
return jsUndefined(); | |
ExceptionCode ec = 0; | |
window->postMessage(message, &messagePorts, targetOrigin, source, ec); | |
setDOMException(exec, ec); | |
return jsUndefined(); | |
} | |
JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args) | |
{ | |
OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, args, currentWorld(exec)); | |
if (exec->hadException()) | |
return jsUndefined(); | |
int delay = args.at(1).toInt32(exec); | |
ExceptionCode ec = 0; | |
int result = impl()->setTimeout(action.release(), delay, ec); | |
setDOMException(exec, ec); | |
return jsNumber(exec, result); | |
} | |
JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args) | |
{ | |
OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, args, currentWorld(exec)); | |
if (exec->hadException()) | |
return jsUndefined(); | |
int delay = args.at(1).toInt32(exec); | |
ExceptionCode ec = 0; | |
int result = impl()->setInterval(action.release(), delay, ec); | |
setDOMException(exec, ec); | |
return jsNumber(exec, result); | |
} | |
JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args) | |
{ | |
Frame* frame = impl()->frame(); | |
if (!frame) | |
return jsUndefined(); | |
JSValue listener = args.at(1); | |
if (!listener.isObject()) | |
return jsUndefined(); | |
impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec)); | |
return jsUndefined(); | |
} | |
JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args) | |
{ | |
Frame* frame = impl()->frame(); | |
if (!frame) | |
return jsUndefined(); | |
JSValue listener = args.at(1); | |
if (!listener.isObject()) | |
return jsUndefined(); | |
impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec)); | |
return jsUndefined(); | |
} | |
#if ENABLE(DATABASE) | |
JSValue JSDOMWindow::openDatabase(ExecState* exec, const ArgList& args) | |
{ | |
if (!allowsAccessFrom(exec) || (args.size() < 4)) | |
return jsUndefined(); | |
ExceptionCode ec = 0; | |
const UString& name = args.at(0).toString(exec); | |
const UString& version = args.at(1).toString(exec); | |
const UString& displayName = args.at(2).toString(exec); | |
unsigned long estimatedSize = args.at(3).toInt32(exec); | |
RefPtr<DatabaseCallback> creationCallback; | |
if ((args.size() >= 5) && args.at(4).isObject()) | |
creationCallback = JSDatabaseCallback::create(asObject(args.at(4)), globalObject()); | |
JSValue result = toJS(exec, globalObject(), WTF::getPtr(impl()->openDatabase(name, version, displayName, estimatedSize, creationCallback.release(), ec))); | |
setDOMException(exec, ec); | |
return result; | |
} | |
#endif | |
DOMWindow* toDOMWindow(JSValue value) | |
{ | |
if (!value.isObject()) | |
return 0; | |
JSObject* object = asObject(value); | |
if (object->inherits(&JSDOMWindow::s_info)) | |
return static_cast<JSDOMWindow*>(object)->impl(); | |
if (object->inherits(&JSDOMWindowShell::s_info)) | |
return static_cast<JSDOMWindowShell*>(object)->impl(); | |
return 0; | |
} | |
} // namespace WebCore |