1 | /* |
2 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
3 | * Copyright (C) 2004-2017 Apple 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 Lesser 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 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | */ |
19 | |
20 | #include "config.h" |
21 | #include "JSPluginElementFunctions.h" |
22 | |
23 | #include "BridgeJSC.h" |
24 | #include "HTMLNames.h" |
25 | #include "HTMLPlugInElement.h" |
26 | #include "JSHTMLElement.h" |
27 | #include "PluginViewBase.h" |
28 | |
29 | |
30 | namespace WebCore { |
31 | using namespace JSC; |
32 | |
33 | using namespace Bindings; |
34 | using namespace HTMLNames; |
35 | |
36 | // JavaScript access to plug-in-exported properties for JSHTMLAppletElement, JSHTMLEmbedElement and JSHTMLObjectElement. |
37 | |
38 | Instance* pluginInstance(HTMLElement& element) |
39 | { |
40 | // The plugin element holds an owning reference, so we don't have to. |
41 | if (!is<HTMLPlugInElement>(element)) |
42 | return nullptr; |
43 | auto* instance = downcast<HTMLPlugInElement>(element).bindingsInstance(); |
44 | if (!instance || !instance->rootObject()) |
45 | return nullptr; |
46 | return instance; |
47 | } |
48 | |
49 | static JSObject* pluginScriptObjectFromPluginViewBase(HTMLPlugInElement& pluginElement, JSGlobalObject* globalObject) |
50 | { |
51 | Widget* pluginWidget = pluginElement.pluginWidget(); |
52 | if (!is<PluginViewBase>(pluginWidget)) |
53 | return nullptr; |
54 | |
55 | return downcast<PluginViewBase>(*pluginWidget).scriptObject(globalObject); |
56 | } |
57 | |
58 | static JSObject* pluginScriptObjectFromPluginViewBase(JSHTMLElement* jsHTMLElement) |
59 | { |
60 | HTMLElement& element = jsHTMLElement->wrapped(); |
61 | if (!is<HTMLPlugInElement>(element)) |
62 | return nullptr; |
63 | |
64 | HTMLPlugInElement& pluginElement = downcast<HTMLPlugInElement>(element); |
65 | return pluginScriptObjectFromPluginViewBase(pluginElement, jsHTMLElement->globalObject()); |
66 | } |
67 | |
68 | JSObject* pluginScriptObject(ExecState* exec, JSHTMLElement* jsHTMLElement) |
69 | { |
70 | HTMLElement& element = jsHTMLElement->wrapped(); |
71 | if (!is<HTMLPlugInElement>(element)) |
72 | return nullptr; |
73 | |
74 | auto& pluginElement = downcast<HTMLPlugInElement>(element); |
75 | |
76 | // Choke point for script/plugin interaction; notify DOMTimer of the event. |
77 | DOMTimer::scriptDidInteractWithPlugin(pluginElement); |
78 | |
79 | // First, see if the element has a plug-in replacement with a script. |
80 | if (auto* scriptObject = pluginElement.scriptObjectForPluginReplacement()) |
81 | return scriptObject; |
82 | |
83 | // Next, see if we can ask the plug-in view for its script object. |
84 | if (auto* scriptObject = pluginScriptObjectFromPluginViewBase(pluginElement, jsHTMLElement->globalObject())) |
85 | return scriptObject; |
86 | |
87 | // Otherwise, fall back to getting the object from the instance. |
88 | |
89 | // The plugin element holds an owning reference, so we don't have to. |
90 | auto* instance = pluginElement.bindingsInstance(); |
91 | if (!instance || !instance->rootObject()) |
92 | return nullptr; |
93 | |
94 | return instance->createRuntimeObject(exec); |
95 | } |
96 | |
97 | static EncodedJSValue pluginElementPropertyGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName) |
98 | { |
99 | VM& vm = exec->vm(); |
100 | auto scope = DECLARE_THROW_SCOPE(vm); |
101 | |
102 | JSHTMLElement* thisObject = jsDynamicCast<JSHTMLElement*>(vm, JSValue::decode(thisValue)); |
103 | if (!thisObject) |
104 | return throwVMTypeError(exec, scope); |
105 | JSObject* scriptObject = pluginScriptObject(exec, thisObject); |
106 | if (!scriptObject) |
107 | return JSValue::encode(jsUndefined()); |
108 | |
109 | return JSValue::encode(scriptObject->get(exec, propertyName)); |
110 | } |
111 | |
112 | bool pluginElementCustomGetOwnPropertySlot(JSHTMLElement* element, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
113 | { |
114 | if (!element->globalObject()->world().isNormal()) { |
115 | JSC::JSValue proto = element->getPrototypeDirect(exec->vm()); |
116 | if (proto.isObject() && JSC::jsCast<JSC::JSObject*>(asObject(proto))->hasProperty(exec, propertyName)) |
117 | return false; |
118 | } |
119 | |
120 | JSObject* scriptObject = pluginScriptObject(exec, element); |
121 | if (!scriptObject) |
122 | return false; |
123 | |
124 | if (!scriptObject->hasProperty(exec, propertyName)) |
125 | return false; |
126 | |
127 | slot.setCustom(element, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum, pluginElementPropertyGetter); |
128 | return true; |
129 | } |
130 | |
131 | bool pluginElementCustomPut(JSHTMLElement* element, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot, bool& putResult) |
132 | { |
133 | JSObject* scriptObject = pluginScriptObject(exec, element); |
134 | if (!scriptObject) |
135 | return false; |
136 | if (!scriptObject->hasProperty(exec, propertyName)) |
137 | return false; |
138 | putResult = scriptObject->methodTable(exec->vm())->put(scriptObject, exec, propertyName, value, slot); |
139 | return true; |
140 | } |
141 | |
142 | static EncodedJSValue JSC_HOST_CALL callPlugin(ExecState* exec) |
143 | { |
144 | JSHTMLElement* element = jsCast<JSHTMLElement*>(exec->jsCallee()); |
145 | |
146 | // Get the plug-in script object. |
147 | JSObject* scriptObject = pluginScriptObject(exec, element); |
148 | ASSERT(scriptObject); |
149 | |
150 | size_t argumentCount = exec->argumentCount(); |
151 | MarkedArgumentBuffer argumentList; |
152 | for (size_t i = 0; i < argumentCount; i++) |
153 | argumentList.append(exec->argument(i)); |
154 | ASSERT(!argumentList.hasOverflowed()); |
155 | |
156 | CallData callData; |
157 | CallType callType = getCallData(exec->vm(), scriptObject, callData); |
158 | ASSERT(callType == CallType::Host); |
159 | |
160 | // Call the object. |
161 | JSValue result = call(exec, scriptObject, callType, callData, exec->thisValue(), argumentList); |
162 | return JSValue::encode(result); |
163 | } |
164 | |
165 | CallType pluginElementCustomGetCallData(JSHTMLElement* element, CallData& callData) |
166 | { |
167 | // First, ask the plug-in view base for its runtime object. |
168 | if (JSObject* scriptObject = pluginScriptObjectFromPluginViewBase(element)) { |
169 | CallData scriptObjectCallData; |
170 | |
171 | VM& vm = *scriptObject->vm(); |
172 | if (scriptObject->methodTable(vm)->getCallData(scriptObject, scriptObjectCallData) == CallType::None) |
173 | return CallType::None; |
174 | |
175 | callData.native.function = callPlugin; |
176 | return CallType::Host; |
177 | } |
178 | |
179 | Instance* instance = pluginInstance(element->wrapped()); |
180 | if (!instance || !instance->supportsInvokeDefaultMethod()) |
181 | return CallType::None; |
182 | callData.native.function = callPlugin; |
183 | return CallType::Host; |
184 | } |
185 | |
186 | } // namespace WebCore |
187 | |