1/*
2 * Copyright (C) 2017 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''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "JSDOMConvertStrings.h"
29
30namespace WebCore {
31
32// Implementations of the abstract operations defined at
33// https://heycam.github.io/webidl/#legacy-platform-object-abstract-ops
34
35enum class OverrideBuiltins {
36 No,
37 Yes
38};
39
40// An implementation of the 'named property visibility algorithm'
41// https://heycam.github.io/webidl/#dfn-named-property-visibility
42template<OverrideBuiltins overrideBuiltins, class JSClass>
43static bool isVisibleNamedProperty(JSC::ExecState& state, JSClass& thisObject, JSC::PropertyName propertyName)
44{
45 // FIXME: It seems unfortunate that have to do two lookups for the property name,
46 // one for isSupportedPropertyName and one by the user of this algorithm to access
47 // that property. It would be nice if we could smuggle the result, or an iterator
48 // out so the duplicate lookup could be avoided.
49
50 // NOTE: While it is not specified, a Symbol can never be a 'supported property
51 // name' so we check that first.
52 if (propertyName.isSymbol())
53 return false;
54
55 auto& impl = thisObject.wrapped();
56
57 // 1. If P is not a supported property name of O, then return false.
58 if (!impl.isSupportedPropertyName(propertyNameToString(propertyName)))
59 return false;
60
61 // 2. If O has an own property named P, then return false.
62 JSC::PropertySlot slot { &thisObject, JSC::PropertySlot::InternalMethodType::VMInquiry };
63 if (JSC::JSObject::getOwnPropertySlot(&thisObject, &state, propertyName, slot))
64 return false;
65
66 // 3. If O implements an interface that has the [OverrideBuiltins] extended attribute, then return true.
67 if (overrideBuiltins == OverrideBuiltins::Yes)
68 return true;
69
70 // 4. Initialize prototype to be the value of the internal [[Prototype]] property of O.
71 // 5. While prototype is not null:
72 // 1. If prototype is not a named properties object, and prototype has an own property named P, then return false.
73 // FIXME: Implement checking for 'named properties object'.
74 // 2. Set prototype to be the value of the internal [[Prototype]] property of prototype.
75 auto prototype = thisObject.getPrototypeDirect(state.vm());
76 if (prototype.isObject() && JSC::asObject(prototype)->getPropertySlot(&state, propertyName, slot))
77 return false;
78
79 // 6. Return true.
80 return true;
81}
82
83// An implementation of the 'named property visibility algorithm' augmented to replace the
84// 'supported property name' check with direct access to the implementation value returned
85// for the property name, via passed in functor. This allows us to avoid two looking up the
86// the property name twice; once for 'named property visibility algorithm' check, and then
87// again when the value is needed.
88template<OverrideBuiltins overrideBuiltins, class JSClass, class Functor>
89static auto accessVisibleNamedProperty(JSC::ExecState& state, JSClass& thisObject, JSC::PropertyName propertyName, Functor&& itemAccessor) -> decltype(itemAccessor(thisObject, propertyName))
90{
91 // NOTE: While it is not specified, a Symbol can never be a 'supported property
92 // name' so we check that first.
93 if (propertyName.isSymbol())
94 return WTF::nullopt;
95
96 // 1. If P is not a supported property name of O, then return false.
97 auto result = itemAccessor(thisObject, propertyName);
98 if (!result)
99 return WTF::nullopt;
100
101 // 2. If O has an own property named P, then return false.
102 JSC::PropertySlot slot { &thisObject, JSC::PropertySlot::InternalMethodType::VMInquiry };
103 if (JSC::JSObject::getOwnPropertySlot(&thisObject, &state, propertyName, slot))
104 return WTF::nullopt;
105
106 // 3. If O implements an interface that has the [OverrideBuiltins] extended attribute, then return true.
107 if (overrideBuiltins == OverrideBuiltins::Yes && !worldForDOMObject(thisObject).shouldDisableOverrideBuiltinsBehavior())
108 return result;
109
110 // 4. Initialize prototype to be the value of the internal [[Prototype]] property of O.
111 // 5. While prototype is not null:
112 // 1. If prototype is not a named properties object, and prototype has an own property named P, then return false.
113 // FIXME: Implement checking for 'named properties object'.
114 // 2. Set prototype to be the value of the internal [[Prototype]] property of prototype.
115 auto prototype = thisObject.getPrototypeDirect(state.vm());
116 if (prototype.isObject() && JSC::asObject(prototype)->getPropertySlot(&state, propertyName, slot))
117 return WTF::nullopt;
118
119 // 6. Return true.
120 return result;
121}
122
123}
124