1/*
2 * Copyright (C) 2003, 2006, 2016 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(NETSCAPE_PLUGIN_API)
29
30#include "c_instance.h"
31
32#include "CRuntimeObject.h"
33#include "IdentifierRep.h"
34#include "JSDOMBinding.h"
35#include "c_class.h"
36#include "c_runtime.h"
37#include "c_utility.h"
38#include "npruntime_impl.h"
39#include "runtime_method.h"
40#include "runtime_root.h"
41#include <JavaScriptCore/ArgList.h>
42#include <JavaScriptCore/CallFrame.h>
43#include <JavaScriptCore/Error.h>
44#include <JavaScriptCore/FunctionPrototype.h>
45#include <JavaScriptCore/JSLock.h>
46#include <JavaScriptCore/PropertyNameArray.h>
47#include <wtf/Assertions.h>
48#include <wtf/NeverDestroyed.h>
49#include <wtf/StdLibExtras.h>
50#include <wtf/Vector.h>
51
52using namespace WebCore;
53
54namespace JSC {
55namespace Bindings {
56
57static String& globalExceptionString()
58{
59 static NeverDestroyed<String> exceptionStr;
60 return exceptionStr;
61}
62
63void CInstance::setGlobalException(String exception)
64{
65 globalExceptionString() = exception;
66}
67
68void CInstance::moveGlobalExceptionToExecState(ExecState* exec)
69{
70 if (globalExceptionString().isNull())
71 return;
72
73 {
74 VM& vm = exec->vm();
75 JSLockHolder lock(vm);
76 auto scope = DECLARE_THROW_SCOPE(vm);
77 throwException(exec, scope, createError(exec, globalExceptionString()));
78 }
79
80 globalExceptionString() = String();
81}
82
83CInstance::CInstance(NPObject* o, RefPtr<RootObject>&& rootObject)
84 : Instance(WTFMove(rootObject))
85{
86 _object = _NPN_RetainObject(o);
87 _class = 0;
88}
89
90CInstance::~CInstance()
91{
92 _NPN_ReleaseObject(_object);
93}
94
95RuntimeObject* CInstance::newRuntimeObject(ExecState* exec)
96{
97 // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object.
98 return CRuntimeObject::create(exec->vm(), WebCore::deprecatedGetDOMStructure<CRuntimeObject>(exec), this);
99}
100
101Class *CInstance::getClass() const
102{
103 if (!_class)
104 _class = CClass::classForIsA(_object->_class);
105 return _class;
106}
107
108bool CInstance::supportsInvokeDefaultMethod() const
109{
110 return _object->_class->invokeDefault;
111}
112
113class CRuntimeMethod : public RuntimeMethod {
114public:
115 typedef RuntimeMethod Base;
116
117 static CRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
118 {
119 VM& vm = globalObject->vm();
120 // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
121 // We need to pass in the right global object for "i".
122 Structure* domStructure = WebCore::deprecatedGetDOMStructure<CRuntimeMethod>(exec);
123 CRuntimeMethod* runtimeMethod = new (NotNull, allocateCell<CRuntimeMethod>(vm.heap)) CRuntimeMethod(globalObject, domStructure, method);
124 runtimeMethod->finishCreation(vm, name);
125 return runtimeMethod;
126 }
127
128 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
129 {
130 return Structure::create(vm, globalObject, prototype, TypeInfo(InternalFunctionType, StructureFlags), info());
131 }
132
133 DECLARE_INFO;
134
135private:
136 CRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
137 : RuntimeMethod(globalObject, structure, method)
138 {
139 }
140
141 void finishCreation(VM& vm, const String& name)
142 {
143 Base::finishCreation(vm, name);
144 ASSERT(inherits(vm, info()));
145 }
146
147};
148
149const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(CRuntimeMethod) };
150
151JSValue CInstance::getMethod(ExecState* exec, PropertyName propertyName)
152{
153 Method* method = getClass()->methodNamed(propertyName, this);
154 return CRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
155}
156
157JSValue CInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
158{
159 VM& vm = exec->vm();
160 auto scope = DECLARE_THROW_SCOPE(vm);
161
162 if (!asObject(runtimeMethod)->inherits<CRuntimeMethod>(vm))
163 return throwTypeError(exec, scope, "Attempt to invoke non-plug-in method on plug-in object."_s);
164
165 CMethod* method = static_cast<CMethod*>(runtimeMethod->method());
166 ASSERT(method);
167
168 NPIdentifier ident = method->identifier();
169 if (!_object->_class->hasMethod(_object, ident))
170 return jsUndefined();
171
172 unsigned count = exec->argumentCount();
173 Vector<NPVariant, 8> cArgs(count);
174
175 unsigned i;
176 for (i = 0; i < count; i++)
177 convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
178
179 // Invoke the 'C' method.
180 bool retval = true;
181 NPVariant resultVariant;
182 VOID_TO_NPVARIANT(resultVariant);
183
184 {
185 JSLock::DropAllLocks dropAllLocks(exec);
186 ASSERT(globalExceptionString().isNull());
187 retval = _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant);
188 moveGlobalExceptionToExecState(exec);
189 }
190
191 if (!retval)
192 throwException(exec, scope, createError(exec, "Error calling method on NPObject."_s));
193
194 for (i = 0; i < count; i++)
195 _NPN_ReleaseVariantValue(&cArgs[i]);
196
197 JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
198 _NPN_ReleaseVariantValue(&resultVariant);
199 return resultValue;
200}
201
202
203JSValue CInstance::invokeDefaultMethod(ExecState* exec)
204{
205 VM& vm = exec->vm();
206 auto scope = DECLARE_THROW_SCOPE(vm);
207
208 if (!_object->_class->invokeDefault)
209 return jsUndefined();
210
211 unsigned count = exec->argumentCount();
212 Vector<NPVariant, 8> cArgs(count);
213
214 unsigned i;
215 for (i = 0; i < count; i++)
216 convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
217
218 // Invoke the 'C' method.
219 bool retval = true;
220 NPVariant resultVariant;
221 VOID_TO_NPVARIANT(resultVariant);
222 {
223 JSLock::DropAllLocks dropAllLocks(exec);
224 ASSERT(globalExceptionString().isNull());
225 retval = _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant);
226 moveGlobalExceptionToExecState(exec);
227 }
228
229 if (!retval)
230 throwException(exec, scope, createError(exec, "Error calling method on NPObject."_s));
231
232 for (i = 0; i < count; i++)
233 _NPN_ReleaseVariantValue(&cArgs[i]);
234
235 JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
236 _NPN_ReleaseVariantValue(&resultVariant);
237 return resultValue;
238}
239
240bool CInstance::supportsConstruct() const
241{
242 return _object->_class->construct;
243}
244
245JSValue CInstance::invokeConstruct(ExecState* exec, const ArgList& args)
246{
247 VM& vm = exec->vm();
248 auto scope = DECLARE_THROW_SCOPE(vm);
249
250 if (!_object->_class->construct)
251 return jsUndefined();
252
253 unsigned count = args.size();
254 Vector<NPVariant, 8> cArgs(count);
255
256 unsigned i;
257 for (i = 0; i < count; i++)
258 convertValueToNPVariant(exec, args.at(i), &cArgs[i]);
259
260 // Invoke the 'C' method.
261 bool retval = true;
262 NPVariant resultVariant;
263 VOID_TO_NPVARIANT(resultVariant);
264 {
265 JSLock::DropAllLocks dropAllLocks(exec);
266 ASSERT(globalExceptionString().isNull());
267 retval = _object->_class->construct(_object, cArgs.data(), count, &resultVariant);
268 moveGlobalExceptionToExecState(exec);
269 }
270
271 if (!retval)
272 throwException(exec, scope, createError(exec, "Error calling method on NPObject."_s));
273
274 for (i = 0; i < count; i++)
275 _NPN_ReleaseVariantValue(&cArgs[i]);
276
277 JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
278 _NPN_ReleaseVariantValue(&resultVariant);
279 return resultValue;
280}
281
282JSValue CInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
283{
284 if (hint == PreferString)
285 return stringValue(exec);
286 if (hint == PreferNumber)
287 return numberValue(exec);
288 return valueOf(exec);
289}
290
291JSValue CInstance::stringValue(ExecState* exec) const
292{
293 JSValue value;
294 if (toJSPrimitive(exec, "toString", value))
295 return value;
296
297 // Fallback to default implementation.
298 return jsNontrivialString(exec, "NPObject"_s);
299}
300
301JSValue CInstance::numberValue(ExecState*) const
302{
303 // FIXME: Implement something sensible.
304 return jsNumber(0);
305}
306
307JSValue CInstance::booleanValue() const
308{
309 // As per ECMA 9.2.
310 return jsBoolean(getObject());
311}
312
313JSValue CInstance::valueOf(ExecState* exec) const
314{
315 JSValue value;
316 if (toJSPrimitive(exec, "valueOf", value))
317 return value;
318
319 // Fallback to default implementation.
320 return stringValue(exec);
321}
322
323bool CInstance::toJSPrimitive(ExecState* exec, const char* name, JSValue& resultValue) const
324{
325 VM& vm = exec->vm();
326 auto scope = DECLARE_THROW_SCOPE(vm);
327
328 NPIdentifier ident = _NPN_GetStringIdentifier(name);
329 if (!_object->_class->hasMethod(_object, ident))
330 return false;
331
332 // Invoke the 'C' method.
333 bool retval = true;
334 NPVariant resultVariant;
335 VOID_TO_NPVARIANT(resultVariant);
336
337 {
338 JSLock::DropAllLocks dropAllLocks(exec);
339 ASSERT(globalExceptionString().isNull());
340 retval = _object->_class->invoke(_object, ident, 0, 0, &resultVariant);
341 moveGlobalExceptionToExecState(exec);
342 }
343
344 if (!retval)
345 throwException(exec, scope, createError(exec, "Error calling method on NPObject."_s));
346
347 resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
348 _NPN_ReleaseVariantValue(&resultVariant);
349 return true;
350}
351
352void CInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
353{
354 if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || !_object->_class->enumerate)
355 return;
356
357 uint32_t count;
358 NPIdentifier* identifiers;
359
360 {
361 JSLock::DropAllLocks dropAllLocks(exec);
362 ASSERT(globalExceptionString().isNull());
363 bool ok = _object->_class->enumerate(_object, &identifiers, &count);
364 moveGlobalExceptionToExecState(exec);
365 if (!ok)
366 return;
367 }
368
369 for (uint32_t i = 0; i < count; i++) {
370 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
371
372 if (identifier->isString())
373 nameArray.add(identifierFromNPIdentifier(exec, identifier->string()));
374 else
375 nameArray.add(Identifier::from(exec, identifier->number()));
376 }
377
378 // FIXME: This should really call NPN_MemFree but that's in WebKit
379 free(identifiers);
380}
381
382}
383}
384
385#endif // ENABLE(NETSCAPE_PLUGIN_API)
386