1/*
2 * Copyright (C) 2018 Apple Inc. All rights reserved.
3
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#pragma once
26
27#include "DOMWrapperWorld.h"
28#include "JSDOMWrapper.h"
29#include <JavaScriptCore/JSCJSValue.h>
30#include <JavaScriptCore/SlotVisitor.h>
31#include <JavaScriptCore/Weak.h>
32#include <wtf/Variant.h>
33
34namespace WebCore {
35
36class JSValueInWrappedObject {
37public:
38 JSValueInWrappedObject(JSC::JSValue = { });
39 JSValueInWrappedObject(const JSValueInWrappedObject&);
40 operator JSC::JSValue() const;
41 explicit operator bool() const;
42 JSValueInWrappedObject& operator=(const JSValueInWrappedObject& other);
43 void visit(JSC::SlotVisitor&) const;
44 void clear();
45
46private:
47 // Use a weak pointer here so that if this code or client code has a visiting mistake,
48 // we get null rather than a dangling pointer to a deleted object.
49 using Weak = JSC::Weak<JSC::JSCell>;
50 // FIXME: Would storing a separate JSValue alongside a Weak be better than using a Variant?
51 using Value = Variant<JSC::JSValue, Weak>;
52 static Value makeValue(JSC::JSValue);
53 Value m_value;
54};
55
56JSC::JSValue cachedPropertyValue(JSC::ExecState&, const JSDOMObject& owner, JSValueInWrappedObject& cacheSlot, const WTF::Function<JSC::JSValue()>&);
57
58inline auto JSValueInWrappedObject::makeValue(JSC::JSValue value) -> Value
59{
60 if (!value.isCell())
61 return value;
62 // FIXME: This is not quite right. It is possible that this value is being
63 // stored in a wrapped object that does not yet have a wrapper. If garbage
64 // collection occurs before the wrapped object gets a wrapper, it's possible
65 // the value object could be collected, and this will become null. A future
66 // version of this class should prevent the value from being collected in
67 // that case. Unclear if this can actually happen in practice.
68 return Weak { value.asCell() };
69}
70
71inline JSValueInWrappedObject::JSValueInWrappedObject(JSC::JSValue value)
72 : m_value(makeValue(JSC::JSValue(value)))
73{
74}
75
76inline JSValueInWrappedObject::JSValueInWrappedObject(const JSValueInWrappedObject& value)
77 : m_value(makeValue(value))
78{
79}
80
81inline JSValueInWrappedObject::operator JSC::JSValue() const
82{
83 return WTF::switchOn(m_value, [] (JSC::JSValue value) {
84 return value;
85 }, [] (const Weak& value) {
86 return value.get();
87 });
88}
89
90inline JSValueInWrappedObject::operator bool() const
91{
92 return JSC::JSValue { *this }.operator bool();
93}
94
95inline JSValueInWrappedObject& JSValueInWrappedObject::operator=(const JSValueInWrappedObject& other)
96{
97 m_value = makeValue(JSC::JSValue(other));
98 return *this;
99}
100
101inline void JSValueInWrappedObject::visit(JSC::SlotVisitor& visitor) const
102{
103 return WTF::switchOn(m_value, [] (JSC::JSValue) {
104 // Nothing to visit.
105 }, [&visitor] (const Weak& value) {
106 visitor.append(value);
107 });
108}
109
110inline void JSValueInWrappedObject::clear()
111{
112 WTF::switchOn(m_value, [] (Weak& value) {
113 value.clear();
114 }, [] (auto&) { });
115}
116
117inline JSC::JSValue cachedPropertyValue(JSC::ExecState& state, const JSDOMObject& owner, JSValueInWrappedObject& cachedValue, const WTF::Function<JSC::JSValue()>& function)
118{
119 if (cachedValue && isWorldCompatible(state, cachedValue))
120 return cachedValue;
121 cachedValue = cloneAcrossWorlds(state, owner, function());
122 ASSERT(isWorldCompatible(state, cachedValue));
123 return cachedValue;
124}
125
126} // namespace WebCore
127