diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/bindings/js/JSDOMWindowCustom.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/bindings/js/JSDOMWindowCustom.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/JSDOMWindowCustom.cpp | 821 |
1 files changed, 383 insertions, 438 deletions
diff --git a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp index 795da5a49..c00484af5 100644 --- a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp +++ b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007-2017 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -21,117 +21,74 @@ #include "config.h" #include "JSDOMWindowCustom.h" +#include "DOMWindowIndexedDatabase.h" #include "Frame.h" #include "HTMLCollection.h" #include "HTMLDocument.h" +#include "HTMLFrameOwnerElement.h" +#include "JSDOMBindingSecurity.h" #include "JSEvent.h" #include "JSEventListener.h" #include "JSHTMLAudioElement.h" #include "JSHTMLCollection.h" #include "JSHTMLOptionElement.h" -#include "JSImageConstructor.h" -#include "JSMessagePortCustom.h" +#include "JSIDBFactory.h" #include "JSWorker.h" #include "Location.h" +#include "RuntimeEnabledFeatures.h" #include "ScheduledAction.h" #include "Settings.h" -#include "SharedWorkerRepository.h" +#include <runtime/JSCInlines.h> +#include <runtime/Lookup.h> -#if ENABLE(SHARED_WORKERS) -#include "JSSharedWorker.h" -#endif - -#if ENABLE(IOS_TOUCH_EVENTS) -#include "JSTouchConstructorIOS.h" -#include "JSTouchListConstructorIOS.h" -#endif - -#if ENABLE(WEB_AUDIO) -#include "JSAudioContext.h" -#endif - -#if ENABLE(WEB_SOCKETS) -#include "JSWebSocket.h" +#if ENABLE(USER_MESSAGE_HANDLERS) +#include "JSWebKitNamespace.h" #endif using namespace JSC; namespace WebCore { -void JSDOMWindow::visitChildren(JSCell* cell, SlotVisitor& visitor) -{ - JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); - ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); - Base::visitChildren(thisObject, visitor); - - thisObject->impl().visitJSEventListeners(visitor); - if (Frame* frame = thisObject->impl().frame()) - visitor.addOpaqueRoot(frame); -} - -template<NativeFunction nativeFunction, int length> -EncodedJSValue nonCachingStaticFunctionGetter(ExecState* exec, EncodedJSValue, EncodedJSValue, PropertyName propertyName) -{ - return JSValue::encode(JSFunction::create(exec->vm(), exec->lexicalGlobalObject(), length, propertyName.publicName(), nativeFunction)); -} +EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionShowModalDialog(ExecState*); -static EncodedJSValue childFrameGetter(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, PropertyName propertyName) +void JSDOMWindow::visitAdditionalChildren(SlotVisitor& visitor) { - return JSValue::encode(toJS(exec, jsCast<JSDOMWindow*>(JSValue::decode(slotBase))->impl().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))->document()->domWindow())); -} - -static EncodedJSValue indexGetter(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, unsigned index) -{ - return JSValue::encode(toJS(exec, jsCast<JSDOMWindow*>(JSValue::decode(slotBase))->impl().frame()->tree().scopedChild(index)->document()->domWindow())); + if (Frame* frame = wrapped().frame()) + visitor.addOpaqueRoot(frame); + + // Normally JSEventTargetCustom.cpp's JSEventTarget::visitAdditionalChildren() would call this. But + // even though DOMWindow is an EventTarget, JSDOMWindow does not subclass JSEventTarget, so we need + // to do this here. + wrapped().visitJSEventListeners(visitor); } -static EncodedJSValue namedItemGetter(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, PropertyName propertyName) +#if ENABLE(USER_MESSAGE_HANDLERS) +static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, EncodedJSValue thisValue, PropertyName) { - JSDOMWindowBase* thisObj = jsCast<JSDOMWindow*>(JSValue::decode(slotBase)); - Document* document = thisObj->impl().frame()->document(); - - ASSERT(BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObj->impl())); - ASSERT(document); - ASSERT(document->isHTMLDocument()); - - AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); - if (!atomicPropertyName || !toHTMLDocument(document)->hasWindowNamedItem(*atomicPropertyName)) + VM& vm = exec->vm(); + JSDOMWindow* castedThis = toJSDOMWindow(vm, JSValue::decode(thisValue)); + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped())) return JSValue::encode(jsUndefined()); - - if (UNLIKELY(toHTMLDocument(document)->windowNamedItemContainsMultipleElements(*atomicPropertyName))) { - RefPtr<HTMLCollection> collection = document->windowNamedItems(atomicPropertyName); - ASSERT(collection->length() > 1); - return JSValue::encode(toJS(exec, thisObj->globalObject(), WTF::getPtr(collection))); - } - - return JSValue::encode(toJS(exec, thisObj->globalObject(), toHTMLDocument(document)->windowNamedItem(*atomicPropertyName))); + return JSValue::encode(toJS(exec, castedThis->globalObject(), castedThis->wrapped().webkitNamespace())); } +#endif -bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) +static bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMWindow* thisObject, Frame* frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot, const String& errorMessage) { - JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); - // 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 frameless window (i.e. one whose page got closed, - // or whose iframe got removed). - // FIXME: This doesn't fully match Firefox, which allows at least toString in addition to those. - if (!thisObject->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() & JSC::Function) && entry->propertyGetter() == jsDOMWindowClosed) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, entry->propertyGetter()); + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + // We don't want any properties other than "close" and "closed" on a frameless window + // (i.e. one whose page got closed, or whose iframe got removed). + // FIXME: This handling for frameless windows duplicates similar behaviour for cross-origin + // access below; we should try to find a way to merge the two. + if (!frame) { + if (propertyName == exec->propertyNames().closed) { + slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowClosed); return true; } - entry = JSDOMWindowPrototype::info()->propHashTable(exec)->entry(exec, propertyName); - if (entry && (entry->attributes() & JSC::Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); + if (propertyName == exec->propertyNames().close) { + slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>); return true; } @@ -141,240 +98,186 @@ bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* exec, Property 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 = shouldAllowAccessToDOMWindow(exec, thisObject->impl(), 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(thisObject, exec, propertyName, slot)) + // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-) + if (propertyName == exec->propertyNames().toStringTagSymbol || propertyName == exec->propertyNames().hasInstanceSymbol || propertyName == exec->propertyNames().isConcatSpreadableSymbol) { + slot.setValue(thisObject, ReadOnly | DontEnum, jsUndefined()); 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 (shouldAllowAccessToDOMWindow) 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::info()->propHashTable(exec)->entry(exec, propertyName); - if (entry) { - if (entry->attributes() & JSC::Function) { - if (entry->function() == jsDOMWindowPrototypeFunctionBlur) { - if (!allowsAccess) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>); - return true; - } - } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) { - if (!allowsAccess) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>); - return true; - } - } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) { - if (!allowsAccess) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>); - return true; - } - } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) { - if (!allowsAccess) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>); - return true; - } - } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) { - if (!DOMWindow::canShowModalDialog(thisObject->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(thisObject, ReadOnly | DontDelete | DontEnum, objectToStringFunctionGetter); - return true; - } - } } - entry = JSDOMWindow::info()->propHashTable(exec)->entry(exec, propertyName); - if (entry) { - slot.setCustom(thisObject, allowsAccess ? entry->attributes() : ReadOnly | DontDelete | DontEnum, entry->propertyGetter()); + // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL). + // Always provide the original function, on a fresh uncached function object. + if (propertyName == exec->propertyNames().blur) { + slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionBlur, 0>); 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 (thisObject->impl().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter); + if (propertyName == exec->propertyNames().close) { + slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>); 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 = thisObject->prototype(); - if (proto.isObject()) { - if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) { - if (!allowsAccess) { - thisObject->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) - unsigned i = propertyName.asIndex(); - if (i < thisObject->impl().frame()->tree().scopedChildCount()) { - ASSERT(i != PropertyName::NotAnIndex); - slot.setCustomIndex(thisObject, ReadOnly | DontDelete | DontEnum, i, indexGetter); + if (propertyName == exec->propertyNames().focus) { + slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionFocus, 0>); return true; } - - if (!allowsAccess) { - thisObject->printErrorMessage(errorMessage); - slot.setUndefined(); + if (propertyName == exec->propertyNames().postMessage) { + slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>); return true; } - // Allow shortcuts like 'Image1' instead of document.images.Image1 - Document* document = thisObject->impl().frame()->document(); - if (document->isHTMLDocument()) { - AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); - if (atomicPropertyName && toHTMLDocument(document)->hasWindowNamedItem(*atomicPropertyName)) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, namedItemGetter); + // When accessing cross-origin known Window properties, we always use the original property getter, + // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's + // behavior. + if (auto* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName)) { + // Only allow access to these specific properties. + if (propertyName == exec->propertyNames().location + || propertyName == exec->propertyNames().closed + || propertyName == exec->propertyNames().length + || propertyName == exec->propertyNames().self + || propertyName == exec->propertyNames().window + || propertyName == exec->propertyNames().frames + || propertyName == exec->propertyNames().opener + || propertyName == exec->propertyNames().parent + || propertyName == exec->propertyNames().top) { + bool shouldExposeSetter = propertyName == exec->propertyNames().location; + CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, entry->propertyGetter(), shouldExposeSetter ? entry->propertyPutter() : nullptr); + slot.setCustomGetterSetter(thisObject, DontEnum | CustomAccessor, customGetterSetter); return true; } + + // For any other entries in the static property table, deny access. (Early return also prevents + // named getter from returning frames with matching names - this seems a little questionable, see + // FIXME comment on prototype search below.) + throwSecurityError(*exec, scope, errorMessage); + slot.setUndefined(); + return false; } - return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); + // 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 (auto* scopedChild = frame->tree().scopedChild(propertyNameToAtomicString(propertyName))) { + slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow())); + return true; + } + + throwSecurityError(*exec, scope, errorMessage); + slot.setUndefined(); + return false; } -bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned index, PropertySlot& slot) +// Property access sequence is: +// (1) indexed properties, +// (2) regular own properties, +// (3) named properties (in fact, these shouldn't be on the window, should be on the NPO). +bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot) { - JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); - - if (!thisObject->impl().frame()) { - // FIXME: We should have a message here that explains why the property access/function call was - // not allowed. - slot.setUndefined(); - return true; - } + // (1) First, indexed properties. + // Hand off all indexed access to getOwnPropertySlotByIndex, which supports the indexed getter. + if (std::optional<unsigned> index = parseIndex(propertyName)) + return getOwnPropertySlotByIndex(object, state, index.value(), slot); - // 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 = shouldAllowAccessToDOMWindow(exec, thisObject->impl(), errorMessage); + auto* thisObject = jsCast<JSDOMWindow*>(object); + auto* frame = thisObject->wrapped().frame(); - // Look for overrides before looking at any of our own properties, but ignore overrides completely - // if this is cross-domain access. - if (allowsAccess && JSGlobalObject::getOwnPropertySlotByIndex(thisObject, exec, index, slot)) - return true; - - PropertyName propertyName = Identifier::from(exec, index); - - // 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 (thisObject->impl().frame()->tree().scopedChild(propertyNameToAtomicString(propertyName))) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, childFrameGetter); - return true; - } + // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess. + String errorMessage; + if (!frame || !BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) + return jsDOMWindowGetOwnPropertySlotRestrictedAccess(thisObject, frame, state, propertyName, slot, errorMessage); - // Do prototype lookup early so that functions and attributes in the prototype can have - // precedence over the index and name getters. - JSValue proto = thisObject->prototype(); - if (proto.isObject()) { - if (asObject(proto)->getPropertySlot(exec, index, slot)) { - if (!allowsAccess) { - thisObject->printErrorMessage(errorMessage); - slot.setUndefined(); - } + // FIXME: this need more explanation. + // (Particularly, is it correct that this exists here but not in getOwnPropertySlotByIndex?) + slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints); + + // (2) Regular own properties. + PropertySlot slotCopy = slot; + if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot)) { + // Detect when we're getting the property 'showModalDialog', this is disabled, and has its original value. + bool isShowModalDialogAndShouldHide = propertyName == state->propertyNames().showModalDialog + && !DOMWindow::canShowModalDialog(*frame) + && slot.isValue() && isHostFunction(slot.getValue(state, propertyName), jsDOMWindowInstanceFunctionShowModalDialog); + // Unless we're in the showModalDialog special case, we're done. + if (!isShowModalDialogAndShouldHide) return true; - } + slot = slotCopy; } - // 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) - if (index < thisObject->impl().frame()->tree().scopedChildCount()) { - ASSERT(index != PropertyName::NotAnIndex); - slot.setCustomIndex(thisObject, ReadOnly | DontDelete | DontEnum, index, indexGetter); +#if ENABLE(USER_MESSAGE_HANDLERS) + if (propertyName == state->propertyNames().webkit && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) { + slot.setCacheableCustom(thisObject, DontDelete | ReadOnly, jsDOMWindowWebKit); return true; } +#endif - if (!allowsAccess) { - thisObject->printErrorMessage(errorMessage); - slot.setUndefined(); + return false; +} + +// Property access sequence is: +// (1) indexed properties, +// (2) regular own properties, +// (3) named properties (in fact, these shouldn't be on the window, should be on the NPO). +bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot) +{ + auto* thisObject = jsCast<JSDOMWindow*>(object); + auto* frame = thisObject->wrapped().frame(); + + // Indexed getters take precendence over regular properties, so caching would be invalid. + slot.disableCaching(); + + // (1) First, indexed properties. + // These are also allowed cross-orgin, so come before the access check. + if (frame && index < frame->tree().scopedChildCount()) { + slot.setValue(thisObject, ReadOnly | DontEnum, toJS(state, frame->tree().scopedChild(index)->document()->domWindow())); return true; } - // Allow shortcuts like 'Image1' instead of document.images.Image1 - Document* document = thisObject->impl().frame()->document(); - if (document->isHTMLDocument()) { - AtomicStringImpl* atomicPropertyName = findAtomicString(propertyName); - if (atomicPropertyName && toHTMLDocument(document)->hasWindowNamedItem(*atomicPropertyName)) { - slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, namedItemGetter); - return true; - } - } + // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess. + String errorMessage; + if (!frame || !BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) + return jsDOMWindowGetOwnPropertySlotRestrictedAccess(thisObject, frame, state, Identifier::from(state, index), slot, errorMessage); - return Base::getOwnPropertySlotByIndex(thisObject, exec, index, slot); + // (2) Regular own properties. + return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot); } -void JSDOMWindow::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +bool JSDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); - if (!thisObject->impl().frame()) - return; + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); - // Optimization: access JavaScript global variables directly before involving the DOM. - if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { - if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) - JSGlobalObject::put(thisObject, exec, propertyName, value, slot); - return; - } + auto* thisObject = jsCast<JSDOMWindow*>(cell); + if (!thisObject->wrapped().frame()) + return false; - if (lookupPut(exec, propertyName, thisObject, value, *s_info.propHashTable(exec), slot)) - return; + String errorMessage; + if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) { + // We only allow setting "location" attribute cross-origin. + if (propertyName == state->propertyNames().location) { + bool putResult = false; + if (lookupPut(state, propertyName, thisObject, value, *s_info.staticPropHashTable, slot, putResult)) + return putResult; + return false; + } + throwSecurityError(*state, scope, errorMessage); + return false; + } - if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) - Base::put(thisObject, exec, propertyName, value, slot); + return Base::put(thisObject, state, propertyName, value, slot); } -void JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow) +bool JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow) { - JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); - if (!thisObject->impl().frame()) - return; - - PropertyName propertyName = Identifier::from(exec, index); - - // Optimization: access JavaScript global variables directly before involving the DOM. - if (thisObject->JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) { - if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) - JSGlobalObject::putByIndex(thisObject, exec, index, value, shouldThrow); - return; - } + auto* thisObject = jsCast<JSDOMWindow*>(cell); + if (!thisObject->wrapped().frame() || !BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped())) + return false; - if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) - Base::putByIndex(thisObject, exec, index, value, shouldThrow); + return Base::putByIndex(thisObject, exec, index, value, shouldThrow); } bool JSDOMWindow::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); // Only allow deleting properties by frames in the same origin. - if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError)) return false; return Base::deleteProperty(thisObject, exec, propertyName); } @@ -383,26 +286,128 @@ bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell); // Only allow deleting properties by frames in the same origin. - if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError)) return false; return Base::deletePropertyByIndex(thisObject, exec, propertyName); } +uint32_t JSDOMWindow::getEnumerableLength(ExecState* exec, JSObject* object) +{ + JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); + // Only allow the window to enumerated by frames in the same origin. + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped())) + return 0; + return Base::getEnumerableLength(exec, thisObject); +} + +void JSDOMWindow::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); + // Only allow the window to enumerated by frames in the same origin. + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped())) + return; + Base::getStructurePropertyNames(thisObject, exec, propertyNames, mode); +} + +void JSDOMWindow::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); + // Only allow the window to enumerated by frames in the same origin. + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped())) + return; + Base::getGenericPropertyNames(thisObject, exec, propertyNames, mode); +} + void JSDOMWindow::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); // Only allow the window to enumerated by frames in the same origin. - if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped())) return; Base::getPropertyNames(thisObject, exec, propertyNames, mode); } +static bool inScope(Frame& frame, TreeScope& scope) +{ + auto* document = frame.document(); + if (!document) + return false; + auto* owner = document->ownerElement(); + return owner && &owner->treeScope() == &scope; +} + +static void addScopedChildrenNames(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames) +{ + auto* document = window.document(); + if (!document) + return; + + auto* frame = document->frame(); + if (!frame) + return; + + for (auto* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) { + if (!inScope(*child, *document)) + continue; + if (!child->tree().name().isEmpty()) + propertyNames.add(Identifier::fromString(&state, child->tree().name())); + } +} + +// https://html.spec.whatwg.org/#crossoriginproperties-(-o-) +static void addCrossOriginPropertyNames(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames) +{ + static const Identifier* const properties[] = { + &state.propertyNames().blur, &state.propertyNames().close, &state.propertyNames().closed, + &state.propertyNames().focus, &state.propertyNames().frames, &state.propertyNames().length, + &state.propertyNames().location, &state.propertyNames().opener, &state.propertyNames().parent, + &state.propertyNames().postMessage, &state.propertyNames().self, &state.propertyNames().top, + &state.propertyNames().window + }; + for (auto* property : properties) + propertyNames.add(*property); + + addScopedChildrenNames(state, window, propertyNames); +} + +static void addScopedChildrenIndexes(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames) +{ + auto* document = window.document(); + if (!document) + return; + + auto* frame = document->frame(); + if (!frame) + return; + + unsigned scopedChildCount = frame->tree().scopedChildCount(); + for (unsigned i = 0; i < scopedChildCount; ++i) + propertyNames.add(Identifier::from(&state, i)); +} + +// https://html.spec.whatwg.org/#crossoriginownpropertykeys-(-o-) +static void addCrossOriginOwnPropertyNames(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames) +{ + addCrossOriginPropertyNames(state, window, propertyNames); + + propertyNames.add(state.propertyNames().toStringTagSymbol); + propertyNames.add(state.propertyNames().hasInstanceSymbol); + propertyNames.add(state.propertyNames().isConcatSpreadableSymbol); +} + +// https://html.spec.whatwg.org/#windowproxy-ownpropertykeys void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); - // Only allow the window to enumerated by frames in the same origin. - if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) + + if (mode.includeDontEnumProperties()) + addScopedChildrenIndexes(*exec, thisObject->wrapped(), propertyNames); + + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) { + if (mode.includeDontEnumProperties()) + addCrossOriginOwnPropertyNames(*exec, thisObject->wrapped(), propertyNames); return; + } Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode); } @@ -410,88 +415,99 @@ bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, { JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced. - if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->impl())) + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError)) return false; // Don't allow shadowing location using accessor properties. - if (descriptor.isAccessorDescriptor() && propertyName == Identifier(exec, "location")) + if (descriptor.isAccessorDescriptor() && propertyName == Identifier::fromString(exec, "location")) return false; return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); } +JSValue JSDOMWindow::getPrototype(JSObject* object, ExecState* exec) +{ + JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object); + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) + return jsNull(); + + return Base::getPrototype(object, exec); +} + +bool JSDOMWindow::preventExtensions(JSObject*, ExecState* exec) +{ + auto scope = DECLARE_THROW_SCOPE(exec->vm()); + + throwTypeError(exec, scope, ASCIILiteral("Cannot prevent extensions on this object")); + return false; +} + +String JSDOMWindow::toStringName(const JSObject* object, ExecState* exec) +{ + auto* thisObject = jsCast<const JSDOMWindow*>(object); + if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) + return ASCIILiteral("Object"); + return ASCIILiteral("Window"); +} + // Custom Attributes -void JSDOMWindow::setLocation(ExecState* exec, JSValue value) +void JSDOMWindow::setLocation(ExecState& state, JSValue value) { + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + #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 (Frame* activeFrame = activeDOMWindow(exec).frame()) { + if (Frame* activeFrame = activeDOMWindow(&state).frame()) { if (activeFrame->settings().usesDashboardBackwardCompatibilityMode() && !activeFrame->tree().parent()) { - if (BindingSecurity::shouldAllowAccessToDOMWindow(exec, impl())) - putDirect(exec->vm(), Identifier(exec, "location"), value); + if (BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped())) + putDirect(state.vm(), Identifier::fromString(&state, "location"), value); return; } } #endif - String locationString = value.toString(exec)->value(exec); - if (exec->hadException()) - return; + String locationString = value.toWTFString(&state); + RETURN_IF_EXCEPTION(scope, void()); - if (Location* location = impl().location()) - location->setHref(locationString, activeDOMWindow(exec), firstDOMWindow(exec)); + if (Location* location = wrapped().location()) + location->setHref(activeDOMWindow(&state), firstDOMWindow(&state), locationString); } -JSValue JSDOMWindow::event(ExecState* exec) const +JSValue JSDOMWindow::event(ExecState& state) const { Event* event = currentEvent(); if (!event) return jsUndefined(); - return toJS(exec, const_cast<JSDOMWindow*>(this), event); -} - -JSValue JSDOMWindow::image(ExecState* exec) const -{ - return getDOMConstructor<JSImageConstructor>(exec->vm(), this); -} - -#if ENABLE(IOS_TOUCH_EVENTS) -JSValue JSDOMWindow::touch(ExecState* exec) const -{ - return getDOMConstructor<JSTouchConstructor>(exec->vm(), this); + return toJS(&state, const_cast<JSDOMWindow*>(this), event); } -JSValue JSDOMWindow::touchList(ExecState* exec) const -{ - return getDOMConstructor<JSTouchListConstructor>(exec->vm(), this); -} -#endif - // Custom functions -JSValue JSDOMWindow::open(ExecState* exec) +JSValue JSDOMWindow::open(ExecState& state) { - String urlString = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0)); - if (exec->hadException()) - return jsUndefined(); - AtomicString frameName = exec->argument(1).isUndefinedOrNull() ? "_blank" : exec->argument(1).toString(exec)->value(exec); - if (exec->hadException()) - return jsUndefined(); - String windowFeaturesString = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2)); - if (exec->hadException()) - return jsUndefined(); - - RefPtr<DOMWindow> openedWindow = impl().open(urlString, frameName, windowFeaturesString, activeDOMWindow(exec), firstDOMWindow(exec)); + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + String urlString = convert<IDLNullable<IDLUSVString>>(state, state.argument(0)); + RETURN_IF_EXCEPTION(scope, JSValue()); + JSValue targetValue = state.argument(1); + AtomicString target = targetValue.isUndefinedOrNull() ? AtomicString("_blank", AtomicString::ConstructFromLiteral) : targetValue.toString(&state)->toAtomicString(&state); + RETURN_IF_EXCEPTION(scope, JSValue()); + String windowFeaturesString = convert<IDLNullable<IDLDOMString>>(state, state.argument(2)); + RETURN_IF_EXCEPTION(scope, JSValue()); + + RefPtr<DOMWindow> openedWindow = wrapped().open(urlString, target, windowFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state)); if (!openedWindow) - return jsUndefined(); - return toJS(exec, openedWindow.get()); + return jsNull(); + return toJS(&state, openedWindow.get()); } class DialogHandler { public: - explicit DialogHandler(ExecState* exec) + explicit DialogHandler(ExecState& exec) : m_exec(exec) { } @@ -500,7 +516,7 @@ public: JSValue returnValue() const; private: - ExecState* m_exec; + ExecState& m_exec; RefPtr<Frame> m_frame; }; @@ -510,162 +526,91 @@ inline void DialogHandler::dialogCreated(DOMWindow& dialog) // FIXME: This looks like a leak between the normal world and an isolated // world if dialogArguments comes from an isolated world. - JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec->vm())); - if (JSValue dialogArguments = m_exec->argument(1)) - globalObject->putDirect(m_exec->vm(), Identifier(m_exec, "dialogArguments"), dialogArguments); + JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm())); + if (JSValue dialogArguments = m_exec.argument(1)) + globalObject->putDirect(m_exec.vm(), Identifier::fromString(&m_exec, "dialogArguments"), dialogArguments); } inline JSValue DialogHandler::returnValue() const { - JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec->vm())); + JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm())); if (!globalObject) return jsUndefined(); - Identifier identifier(m_exec, "returnValue"); - PropertySlot slot(globalObject); - if (!JSGlobalObject::getOwnPropertySlot(globalObject, m_exec, identifier, slot)) + Identifier identifier = Identifier::fromString(&m_exec, "returnValue"); + PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get); + if (!JSGlobalObject::getOwnPropertySlot(globalObject, &m_exec, identifier, slot)) return jsUndefined(); - return slot.getValue(m_exec, identifier); + return slot.getValue(&m_exec, identifier); } -JSValue JSDOMWindow::showModalDialog(ExecState* exec) +JSValue JSDOMWindow::showModalDialog(ExecState& state) { - String urlString = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(0)); - if (exec->hadException()) - return jsUndefined(); - String dialogFeaturesString = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(2)); - if (exec->hadException()) - return jsUndefined(); + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (UNLIKELY(state.argumentCount() < 1)) + return throwException(&state, scope, createNotEnoughArgumentsError(&state)); + + String urlString = convert<IDLNullable<IDLDOMString>>(state, state.argument(0)); + RETURN_IF_EXCEPTION(scope, JSValue()); + String dialogFeaturesString = convert<IDLNullable<IDLDOMString>>(state, state.argument(2)); + RETURN_IF_EXCEPTION(scope, JSValue()); - DialogHandler handler(exec); + DialogHandler handler(state); - impl().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(exec), firstDOMWindow(exec), [&handler](DOMWindow& dialog) { + wrapped().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(&state), firstDOMWindow(&state), [&handler](DOMWindow& dialog) { handler.dialogCreated(dialog); }); return handler.returnValue(); } -static JSValue handlePostMessage(DOMWindow* impl, ExecState* exec) +JSValue JSDOMWindow::setTimeout(ExecState& state) { - MessagePortArray messagePorts; - ArrayBufferArray arrayBuffers; + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); - // This function has variable arguments and can be: - // Per current spec: - // postMessage(message, targetOrigin) - // postMessage(message, targetOrigin, {sequence of transferrables}) - // Legacy non-standard implementations in webkit allowed: - // postMessage(message, {sequence of transferrables}, targetOrigin); - int targetOriginArgIndex = 1; - if (exec->argumentCount() > 2) { - int transferablesArgIndex = 2; - if (exec->argument(2).isString()) { - targetOriginArgIndex = 2; - transferablesArgIndex = 1; - } - fillMessagePortArray(exec, exec->argument(transferablesArgIndex), messagePorts, arrayBuffers); - } - if (exec->hadException()) - return jsUndefined(); - - RefPtr<SerializedScriptValue> message = SerializedScriptValue::create(exec, exec->argument(0), - &messagePorts, - &arrayBuffers); - - if (exec->hadException()) - return jsUndefined(); - - String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, exec->argument(targetOriginArgIndex)); - if (exec->hadException()) - return jsUndefined(); - - ExceptionCode ec = 0; - impl->postMessage(message.release(), &messagePorts, targetOrigin, activeDOMWindow(exec), ec); - setDOMException(exec, ec); - - return jsUndefined(); -} - -JSValue JSDOMWindow::postMessage(ExecState* exec) -{ - return handlePostMessage(&impl(), exec); -} - -JSValue JSDOMWindow::setTimeout(ExecState* exec) -{ - ContentSecurityPolicy* contentSecurityPolicy = impl().document() ? impl().document()->contentSecurityPolicy() : 0; - OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, currentWorld(exec), contentSecurityPolicy); - if (exec->hadException()) - return jsUndefined(); + if (UNLIKELY(state.argumentCount() < 1)) + return throwException(&state, scope, createNotEnoughArgumentsError(&state)); + auto* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr; + auto action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy); + RETURN_IF_EXCEPTION(scope, JSValue()); if (!action) return jsNumber(0); - int delay = exec->argument(1).toInt32(exec); - - ExceptionCode ec = 0; - int result = impl().setTimeout(action.release(), delay, ec); - setDOMException(exec, ec); - - return jsNumber(result); + int delay = state.argument(1).toInt32(&state); + return toJS<IDLLong>(state, scope, wrapped().setTimeout(WTFMove(action), delay)); } -JSValue JSDOMWindow::setInterval(ExecState* exec) +JSValue JSDOMWindow::setInterval(ExecState& state) { - ContentSecurityPolicy* contentSecurityPolicy = impl().document() ? impl().document()->contentSecurityPolicy() : 0; - OwnPtr<ScheduledAction> action = ScheduledAction::create(exec, currentWorld(exec), contentSecurityPolicy); - if (exec->hadException()) - return jsUndefined(); - int delay = exec->argument(1).toInt32(exec); + VM& vm = state.vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (UNLIKELY(state.argumentCount() < 1)) + return throwException(&state, scope, createNotEnoughArgumentsError(&state)); + + auto* contentSecurityPolicy = wrapped().document() ? wrapped().document()->contentSecurityPolicy() : nullptr; + auto action = ScheduledAction::create(&state, globalObject()->world(), contentSecurityPolicy); + RETURN_IF_EXCEPTION(scope, JSValue()); if (!action) return jsNumber(0); - ExceptionCode ec = 0; - int result = impl().setInterval(action.release(), delay, ec); - setDOMException(exec, ec); - - return jsNumber(result); -} - -JSValue JSDOMWindow::addEventListener(ExecState* exec) -{ - Frame* frame = impl().frame(); - if (!frame) - return jsUndefined(); - - JSValue listener = exec->argument(1); - if (!listener.isObject()) - return jsUndefined(); - - impl().addEventListener(exec->argument(0).toString(exec)->value(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), exec->argument(2).toBoolean(exec)); - return jsUndefined(); + int delay = state.argument(1).toInt32(&state); + return toJS<IDLLong>(state, scope, wrapped().setInterval(WTFMove(action), delay)); } -JSValue JSDOMWindow::removeEventListener(ExecState* exec) -{ - Frame* frame = impl().frame(); - if (!frame) - return jsUndefined(); - - JSValue listener = exec->argument(1); - if (!listener.isObject()) - return jsUndefined(); - - impl().removeEventListener(exec->argument(0).toString(exec)->value(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), exec->argument(2).toBoolean(exec)); - return jsUndefined(); -} - -DOMWindow* toDOMWindow(JSValue value) +DOMWindow* JSDOMWindow::toWrapped(VM& vm, JSValue value) { if (!value.isObject()) - return 0; + return nullptr; JSObject* object = asObject(value); - if (object->inherits(JSDOMWindow::info())) - return &jsCast<JSDOMWindow*>(object)->impl(); - if (object->inherits(JSDOMWindowShell::info())) - return &jsCast<JSDOMWindowShell*>(object)->impl(); - return 0; + if (object->inherits(vm, JSDOMWindow::info())) + return &jsCast<JSDOMWindow*>(object)->wrapped(); + if (object->inherits(vm, JSDOMWindowShell::info())) + return &jsCast<JSDOMWindowShell*>(object)->wrapped(); + return nullptr; } } // namespace WebCore |