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
30namespace WebCore {
31using namespace JSC;
32
33using namespace Bindings;
34using namespace HTMLNames;
35
36// JavaScript access to plug-in-exported properties for JSHTMLAppletElement, JSHTMLEmbedElement and JSHTMLObjectElement.
37
38Instance* 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
49static 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
58static 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
68JSObject* 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
97static 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
112bool 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
131bool 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
142static 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
165CallType 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