281ea4e7004593eb6dea541c74b5bca4ae5dfa60
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMWindowCustom.cpp
1 /*
2  * Copyright (C) 2007-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "JSDOMWindowCustom.h"
23
24 #include "DOMWindowIndexedDatabase.h"
25 #include "Frame.h"
26 #include "HTMLCollection.h"
27 #include "HTMLDocument.h"
28 #include "HTMLFrameOwnerElement.h"
29 #include "JSDOMBindingSecurity.h"
30 #include "JSDOMConvertNullable.h"
31 #include "JSDOMConvertNumbers.h"
32 #include "JSDOMConvertStrings.h"
33 #include "JSEvent.h"
34 #include "JSEventListener.h"
35 #include "JSHTMLAudioElement.h"
36 #include "JSHTMLCollection.h"
37 #include "JSHTMLOptionElement.h"
38 #include "JSIDBFactory.h"
39 #include "JSRemoteDOMWindow.h"
40 #include "JSWindowProxy.h"
41 #include "JSWorker.h"
42 #include "Location.h"
43 #include "RuntimeEnabledFeatures.h"
44 #include "ScheduledAction.h"
45 #include "Settings.h"
46 #include "WebCoreJSClientData.h"
47 #include <JavaScriptCore/JSCInlines.h>
48 #include <JavaScriptCore/Lookup.h>
49
50 #if ENABLE(USER_MESSAGE_HANDLERS)
51 #include "JSWebKitNamespace.h"
52 #endif
53
54
55 namespace WebCore {
56 using namespace JSC;
57
58 EncodedJSValue JSC_HOST_CALL jsDOMWindowInstanceFunctionShowModalDialog(ExecState*);
59
60 void JSDOMWindow::visitAdditionalChildren(SlotVisitor& visitor)
61 {
62     if (Frame* frame = wrapped().frame())
63         visitor.addOpaqueRoot(frame);
64     
65     // Normally JSEventTargetCustom.cpp's JSEventTarget::visitAdditionalChildren() would call this. But
66     // even though DOMWindow is an EventTarget, JSDOMWindow does not subclass JSEventTarget, so we need
67     // to do this here.
68     wrapped().visitJSEventListeners(visitor);
69 }
70
71 #if ENABLE(USER_MESSAGE_HANDLERS)
72 static EncodedJSValue jsDOMWindowWebKit(ExecState* exec, EncodedJSValue thisValue, PropertyName)
73 {
74     VM& vm = exec->vm();
75     JSDOMWindow* castedThis = toJSDOMWindow(vm, JSValue::decode(thisValue));
76     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, castedThis->wrapped()))
77         return JSValue::encode(jsUndefined());
78     return JSValue::encode(toJS(exec, castedThis->globalObject(), castedThis->wrapped().webkitNamespace()));
79 }
80 #endif
81
82 template <DOMWindowType windowType>
83 bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMGlobalObject* thisObject, AbstractFrame* frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot, const String& errorMessage)
84 {
85     VM& vm = exec->vm();
86     auto scope = DECLARE_THROW_SCOPE(vm);
87
88     auto& builtinNames = static_cast<JSVMClientData*>(vm.clientData)->builtinNames();
89
90     // https://html.spec.whatwg.org/#crossorigingetownpropertyhelper-(-o,-p-)
91     if (propertyName == vm.propertyNames->toStringTagSymbol || propertyName == vm.propertyNames->hasInstanceSymbol || propertyName == vm.propertyNames->isConcatSpreadableSymbol) {
92         slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum, jsUndefined());
93         return true;
94     }
95
96     // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL).
97     // Always provide the original function, on a fresh uncached function object.
98     if (propertyName == builtinNames.blurPublicName()) {
99         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionBlur, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionBlur, 0>);
100         return true;
101     }
102     if (propertyName == builtinNames.closePublicName()) {
103         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionClose, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>);
104         return true;
105     }
106     if (propertyName == builtinNames.focusPublicName()) {
107         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionFocus, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionFocus, 0>);
108         return true;
109     }
110     if (propertyName == builtinNames.postMessagePublicName()) {
111         slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum), windowType == DOMWindowType::Remote ? nonCachingStaticFunctionGetter<jsRemoteDOMWindowInstanceFunctionPostMessage, 0> : nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>);
112         return true;
113     }
114
115     // When accessing cross-origin known Window properties, we always use the original property getter,
116     // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's
117     // behavior.
118     auto* classInfo = windowType == DOMWindowType::Remote ? JSRemoteDOMWindow::info() : JSDOMWindow::info();
119     if (auto* entry = classInfo->staticPropHashTable->entry(propertyName)) {
120         // Only allow access to these specific properties.
121         if (propertyName == builtinNames.locationPublicName()
122             || propertyName == builtinNames.closedPublicName()
123             || propertyName == vm.propertyNames->length
124             || propertyName == builtinNames.selfPublicName()
125             || propertyName == builtinNames.windowPublicName()
126             || propertyName == builtinNames.framesPublicName()
127             || propertyName == builtinNames.openerPublicName()
128             || propertyName == builtinNames.parentPublicName()
129             || propertyName == builtinNames.topPublicName()) {
130             bool shouldExposeSetter = propertyName == builtinNames.locationPublicName();
131             CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, entry->propertyGetter(), shouldExposeSetter ? entry->propertyPutter() : nullptr);
132             slot.setCustomGetterSetter(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontEnum), customGetterSetter);
133             return true;
134         }
135
136         // For any other entries in the static property table, deny access. (Early return also prevents
137         // named getter from returning frames with matching names - this seems a little questionable, see
138         // FIXME comment on prototype search below.)
139         throwSecurityError(*exec, scope, errorMessage);
140         slot.setUndefined();
141         return false;
142     }
143
144     // Check for child frames by name before built-in properties to match Mozilla. This does
145     // not match IE, but some sites end up naming frames things that conflict with window
146     // properties that are in Moz but not IE. Since we have some of these, we have to do it
147     // the Moz way.
148     // FIXME: Add support to named attributes on RemoteFrames.
149     if (frame && is<Frame>(*frame)) {
150         if (auto* scopedChild = downcast<Frame>(*frame).tree().scopedChild(propertyNameToAtomicString(propertyName))) {
151             slot.setValue(thisObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, toJS(exec, scopedChild->document()->domWindow()));
152             return true;
153         }
154     }
155
156     throwSecurityError(*exec, scope, errorMessage);
157     slot.setUndefined();
158     return false;
159 }
160 template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(JSDOMGlobalObject*, AbstractFrame*, ExecState*, PropertyName, PropertySlot&, const String&);
161 template bool jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Remote>(JSDOMGlobalObject*, AbstractFrame*, ExecState*, PropertyName, PropertySlot&, const String&);
162
163 // Property access sequence is:
164 // (1) indexed properties,
165 // (2) regular own properties,
166 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
167 bool JSDOMWindow::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)
168 {
169     // (1) First, indexed properties.
170     // Hand off all indexed access to getOwnPropertySlotByIndex, which supports the indexed getter.
171     if (std::optional<unsigned> index = parseIndex(propertyName))
172         return getOwnPropertySlotByIndex(object, state, index.value(), slot);
173
174     auto* thisObject = jsCast<JSDOMWindow*>(object);
175     auto* frame = thisObject->wrapped().frame();
176
177     // Hand off all cross-domain access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
178     String errorMessage;
179     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage))
180         return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, frame, state, propertyName, slot, errorMessage);
181     
182     // FIXME: this need more explanation.
183     // (Particularly, is it correct that this exists here but not in getOwnPropertySlotByIndex?)
184     slot.setWatchpointSet(thisObject->m_windowCloseWatchpoints);
185
186     // (2) Regular own properties.
187     PropertySlot slotCopy = slot;
188     if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot)) {
189         // Detect when we're getting the property 'showModalDialog', this is disabled, and has its original value.
190         bool isShowModalDialogAndShouldHide = propertyName == static_cast<JSVMClientData*>(state->vm().clientData)->builtinNames().showModalDialogPublicName()
191             && (!frame || !DOMWindow::canShowModalDialog(*frame))
192             && slot.isValue() && isHostFunction(slot.getValue(state, propertyName), jsDOMWindowInstanceFunctionShowModalDialog);
193         // Unless we're in the showModalDialog special case, we're done.
194         if (!isShowModalDialogAndShouldHide)
195             return true;
196         slot = slotCopy;
197     }
198
199 #if ENABLE(USER_MESSAGE_HANDLERS)
200     if (propertyName == static_cast<JSVMClientData*>(state->vm().clientData)->builtinNames().webkitPublicName() && thisObject->wrapped().shouldHaveWebKitNamespaceForWorld(thisObject->world())) {
201         slot.setCacheableCustom(thisObject, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly, jsDOMWindowWebKit);
202         return true;
203     }
204 #endif
205
206     return false;
207 }
208
209 // Property access sequence is:
210 // (1) indexed properties,
211 // (2) regular own properties,
212 // (3) named properties (in fact, these shouldn't be on the window, should be on the NPO).
213 bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state, unsigned index, PropertySlot& slot)
214 {
215     auto* thisObject = jsCast<JSDOMWindow*>(object);
216     auto* frame = thisObject->wrapped().frame();
217
218     // Indexed getters take precendence over regular properties, so caching would be invalid.
219     slot.disableCaching();
220
221     // (1) First, indexed properties.
222     // These are also allowed cross-orgin, so come before the access check.
223     if (frame && index < frame->tree().scopedChildCount()) {
224         slot.setValue(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly), toJS(state, frame->tree().scopedChild(index)->document()->domWindow()));
225         return true;
226     }
227
228     // Hand off all cross-domain/frameless access to jsDOMWindowGetOwnPropertySlotRestrictedAccess.
229     String errorMessage;
230     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage))
231         return jsDOMWindowGetOwnPropertySlotRestrictedAccess<DOMWindowType::Local>(thisObject, frame, state, Identifier::from(state, index), slot, errorMessage);
232
233     // (2) Regular own properties.
234     return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot);
235 }
236
237 bool JSDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
238 {
239     VM& vm = state->vm();
240     auto scope = DECLARE_THROW_SCOPE(vm);
241
242     auto* thisObject = jsCast<JSDOMWindow*>(cell);
243     if (!thisObject->wrapped().frame())
244         return false;
245
246     String errorMessage;
247     if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) {
248         // We only allow setting "location" attribute cross-origin.
249         if (propertyName == static_cast<JSVMClientData*>(vm.clientData)->builtinNames().locationPublicName()) {
250             bool putResult = false;
251             if (lookupPut(state, propertyName, thisObject, value, *s_info.staticPropHashTable, slot, putResult))
252                 return putResult;
253             return false;
254         }
255         throwSecurityError(*state, scope, errorMessage);
256         return false;
257     }
258
259     return Base::put(thisObject, state, propertyName, value, slot);
260 }
261
262 bool JSDOMWindow::putByIndex(JSCell* cell, ExecState* exec, unsigned index, JSValue value, bool shouldThrow)
263 {
264     auto* thisObject = jsCast<JSDOMWindow*>(cell);
265     if (!thisObject->wrapped().frame() || !BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped()))
266         return false;
267     
268     return Base::putByIndex(thisObject, exec, index, value, shouldThrow);
269 }
270
271 bool JSDOMWindow::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
272 {
273     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
274     // Only allow deleting properties by frames in the same origin.
275     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
276         return false;
277     return Base::deleteProperty(thisObject, exec, propertyName);
278 }
279
280 bool JSDOMWindow::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
281 {
282     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(cell);
283     // Only allow deleting properties by frames in the same origin.
284     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
285         return false;
286     return Base::deletePropertyByIndex(thisObject, exec, propertyName);
287 }
288
289 // https://html.spec.whatwg.org/#crossoriginproperties-(-o-)
290 static void addCrossOriginWindowPropertyNames(VM& vm, PropertyNameArray& propertyNames)
291 {
292     static const Identifier* const properties[] = {
293         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().blurPublicName(),
294         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().closePublicName(),
295         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().closedPublicName(),
296         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().focusPublicName(),
297         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().framesPublicName(),
298         &vm.propertyNames->length,
299         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().locationPublicName(),
300         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().openerPublicName(),
301         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().parentPublicName(),
302         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().postMessagePublicName(),
303         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().selfPublicName(),
304         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().topPublicName(),
305         &static_cast<JSVMClientData*>(vm.clientData)->builtinNames().windowPublicName()
306     };
307     for (auto* property : properties)
308         propertyNames.add(*property);
309 }
310
311 static void addScopedChildrenIndexes(ExecState& state, DOMWindow& window, PropertyNameArray& propertyNames)
312 {
313     auto* document = window.document();
314     if (!document)
315         return;
316
317     auto* frame = document->frame();
318     if (!frame)
319         return;
320
321     unsigned scopedChildCount = frame->tree().scopedChildCount();
322     for (unsigned i = 0; i < scopedChildCount; ++i)
323         propertyNames.add(Identifier::from(&state, i));
324 }
325
326 // https://html.spec.whatwg.org/#crossoriginownpropertykeys-(-o-)
327 void addCrossOriginWindowOwnPropertyNames(ExecState& state, PropertyNameArray& propertyNames)
328 {
329     VM& vm = state.vm();
330     addCrossOriginWindowPropertyNames(vm, propertyNames);
331
332     propertyNames.add(vm.propertyNames->toStringTagSymbol);
333     propertyNames.add(vm.propertyNames->hasInstanceSymbol);
334     propertyNames.add(vm.propertyNames->isConcatSpreadableSymbol);
335 }
336
337 // https://html.spec.whatwg.org/#windowproxy-ownpropertykeys
338 void JSDOMWindow::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
339 {
340     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
341
342     addScopedChildrenIndexes(*exec, thisObject->wrapped(), propertyNames);
343
344     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError)) {
345         if (mode.includeDontEnumProperties())
346             addCrossOriginWindowOwnPropertyNames(*exec, propertyNames);
347         return;
348     }
349     Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
350 }
351
352 bool JSDOMWindow::defineOwnProperty(JSC::JSObject* object, JSC::ExecState* exec, JSC::PropertyName propertyName, const JSC::PropertyDescriptor& descriptor, bool shouldThrow)
353 {
354     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
355     // Only allow defining properties in this way by frames in the same origin, as it allows setters to be introduced.
356     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), ThrowSecurityError))
357         return false;
358
359     // Don't allow shadowing location using accessor properties.
360     if (descriptor.isAccessorDescriptor() && propertyName == Identifier::fromString(exec, "location"))
361         return false;
362
363     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
364 }
365
366 JSValue JSDOMWindow::getPrototype(JSObject* object, ExecState* exec)
367 {
368     JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
369     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError))
370         return jsNull();
371
372     return Base::getPrototype(object, exec);
373 }
374
375 bool JSDOMWindow::preventExtensions(JSObject*, ExecState* exec)
376 {
377     auto scope = DECLARE_THROW_SCOPE(exec->vm());
378
379     throwTypeError(exec, scope, ASCIILiteral("Cannot prevent extensions on this object"));
380     return false;
381 }
382
383 String JSDOMWindow::toStringName(const JSObject* object, ExecState* exec)
384 {
385     auto* thisObject = jsCast<const JSDOMWindow*>(object);
386     if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError))
387         return ASCIILiteral("Object");
388     return ASCIILiteral("Window");
389 }
390
391 // Custom Attributes
392
393 JSValue JSDOMWindow::event(ExecState& state) const
394 {
395     Event* event = currentEvent();
396     if (!event)
397         return jsUndefined();
398     return toJS(&state, const_cast<JSDOMWindow*>(this), event);
399 }
400
401 // Custom functions
402
403 class DialogHandler {
404 public:
405     explicit DialogHandler(ExecState& exec)
406         : m_exec(exec)
407     {
408     }
409
410     void dialogCreated(DOMWindow&);
411     JSValue returnValue() const;
412
413 private:
414     ExecState& m_exec;
415     RefPtr<Frame> m_frame;
416 };
417
418 inline void DialogHandler::dialogCreated(DOMWindow& dialog)
419 {
420     m_frame = dialog.frame();
421     
422     // FIXME: This looks like a leak between the normal world and an isolated
423     //        world if dialogArguments comes from an isolated world.
424     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
425     if (JSValue dialogArguments = m_exec.argument(1))
426         globalObject->putDirect(m_exec.vm(), Identifier::fromString(&m_exec, "dialogArguments"), dialogArguments);
427 }
428
429 inline JSValue DialogHandler::returnValue() const
430 {
431     JSDOMWindow* globalObject = toJSDOMWindow(m_frame.get(), normalWorld(m_exec.vm()));
432     if (!globalObject)
433         return jsUndefined();
434     Identifier identifier = Identifier::fromString(&m_exec, "returnValue");
435     PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get);
436     if (!JSGlobalObject::getOwnPropertySlot(globalObject, &m_exec, identifier, slot))
437         return jsUndefined();
438     return slot.getValue(&m_exec, identifier);
439 }
440
441 JSValue JSDOMWindow::showModalDialog(ExecState& state)
442 {
443     VM& vm = state.vm();
444     auto scope = DECLARE_THROW_SCOPE(vm);
445
446     if (UNLIKELY(state.argumentCount() < 1))
447         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
448
449     String urlString = convert<IDLNullable<IDLDOMString>>(state, state.argument(0));
450     RETURN_IF_EXCEPTION(scope, JSValue());
451     String dialogFeaturesString = convert<IDLNullable<IDLDOMString>>(state, state.argument(2));
452     RETURN_IF_EXCEPTION(scope, JSValue());
453
454     DialogHandler handler(state);
455
456     wrapped().showModalDialog(urlString, dialogFeaturesString, activeDOMWindow(state), firstDOMWindow(state), [&handler](DOMWindow& dialog) {
457         handler.dialogCreated(dialog);
458     });
459
460     return handler.returnValue();
461 }
462
463 DOMWindow* JSDOMWindow::toWrapped(VM& vm, JSValue value)
464 {
465     if (!value.isObject())
466         return nullptr;
467     JSObject* object = asObject(value);
468     if (object->inherits<JSDOMWindow>(vm))
469         return &jsCast<JSDOMWindow*>(object)->wrapped();
470     if (object->inherits<JSWindowProxy>(vm)) {
471         if (auto* jsDOMWindow = jsDynamicCast<JSDOMWindow*>(vm, jsCast<JSWindowProxy*>(object)->window()))
472             return &jsDOMWindow->wrapped();
473     }
474     return nullptr;
475 }
476
477 void JSDOMWindow::setOpener(JSC::ExecState& state, JSC::JSValue value)
478 {
479     if (!BindingSecurity::shouldAllowAccessToDOMWindow(&state, wrapped(), ThrowSecurityError))
480         return;
481
482     if (value.isNull()) {
483         wrapped().disownOpener();
484         return;
485     }
486     replaceStaticPropertySlot(state.vm(), this, Identifier::fromString(&state.vm(), "opener"), value);
487 }
488
489 } // namespace WebCore