/* * Copyright (C) 2018 Apple Inc. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "DOMWrapperWorld.h" #include "JSDOMWrapper.h" #include <JavaScriptCore/JSCJSValue.h> #include <JavaScriptCore/SlotVisitor.h> #include <JavaScriptCore/Weak.h> #include <wtf/Variant.h> namespace WebCore { class JSValueInWrappedObject { public: JSValueInWrappedObject(JSC::JSValue = { }); operator JSC::JSValue() const; explicit operator bool() const; void visit(JSC::SlotVisitor&) const; private: // Use a weak pointer here so that if this code or client code has a visiting mistake, // we get null rather than a dangling pointer to a deleted object. using Weak = JSC::Weak<JSC::JSCell>; // FIXME: Would storing a separate JSValue alongside a Weak be better than using a Variant? using Value = Variant<JSC::JSValue, Weak>; static Value makeValue(JSC::JSValue); Value m_value; }; JSC::JSValue cachedPropertyValue(JSC::ExecState&, const JSDOMObject& owner, JSValueInWrappedObject& cacheSlot, const WTF::Function<JSC::JSValue()>&); inline auto JSValueInWrappedObject::makeValue(JSC::JSValue value) -> Value { if (!value.isCell()) return value; // FIXME: This is not quite right. It is possible that this value is being // stored in a wrapped object that does not yet have a wrapper. If garbage // collection occurs before the wrapped object gets a wrapper, it's possible // the value object could be collected, and this will become null. A future // version of this class should prevent the value from being collected in // that case. Unclear if this can actually happen in practice. return Weak { value.asCell() }; } inline JSValueInWrappedObject::JSValueInWrappedObject(JSC::JSValue value) : m_value(makeValue(value)) { } inline JSValueInWrappedObject::operator JSC::JSValue() const { return WTF::switchOn(m_value, [] (JSC::JSValue value) { return value; }, [] (const Weak& value) { return value.get(); }); } inline JSValueInWrappedObject::operator bool() const { return JSC::JSValue { *this }.operator bool(); } inline void JSValueInWrappedObject::visit(JSC::SlotVisitor& visitor) const { return WTF::switchOn(m_value, [] (JSC::JSValue) { // Nothing to visit. }, [&visitor] (const Weak& value) { visitor.append(value); }); } inline JSC::JSValue cachedPropertyValue(JSC::ExecState& state, const JSDOMObject& owner, JSValueInWrappedObject& cachedValue, const WTF::Function<JSC::JSValue()>& function) { if (cachedValue && isWorldCompatible(state, cachedValue)) return cachedValue; cachedValue = cloneAcrossWorlds(state, owner, function()); ASSERT(isWorldCompatible(state, cachedValue)); return cachedValue; } } // namespace WebCore