Modernize some aspects of text codecs, eliminate WebKit use of strcasecmp
[WebKit-https.git] / Source / WebCore / bridge / c / c_instance.cpp
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 <interpreter/CallFrame.h>
42 #include <runtime/ArgList.h>
43 #include <runtime/Error.h>
44 #include <runtime/FunctionPrototype.h>
45 #include <runtime/JSLock.h>
46 #include <runtime/PropertyNameArray.h>
47 #include <wtf/Assertions.h>
48 #include <wtf/NeverDestroyed.h>
49 #include <wtf/StdLibExtras.h>
50 #include <wtf/Vector.h>
51
52 using namespace WebCore;
53
54 namespace JSC {
55 namespace Bindings {
56
57 static String& globalExceptionString()
58 {
59     static NeverDestroyed<String> exceptionStr;
60     return exceptionStr;
61 }
62
63 void CInstance::setGlobalException(String exception)
64 {
65     globalExceptionString() = exception;
66 }
67
68 void 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
83 CInstance::CInstance(NPObject* o, RefPtr<RootObject>&& rootObject)
84     : Instance(WTFMove(rootObject))
85 {
86     _object = _NPN_RetainObject(o);
87     _class = 0;
88 }
89
90 CInstance::~CInstance()
91 {
92     _NPN_ReleaseObject(_object);
93 }
94
95 RuntimeObject* 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
101 Class *CInstance::getClass() const
102 {
103     if (!_class)
104         _class = CClass::classForIsA(_object->_class);
105     return _class;
106 }
107
108 bool CInstance::supportsInvokeDefaultMethod() const
109 {
110     return _object->_class->invokeDefault;
111 }
112
113 class CRuntimeMethod : public RuntimeMethod {
114 public:
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
135 private:
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
149 const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(CRuntimeMethod) };
150
151 JSValue 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
157 JSValue 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(vm, CRuntimeMethod::info()))
163         return throwTypeError(exec, scope, ASCIILiteral("Attempt to invoke non-plug-in method on plug-in object."));
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, ASCIILiteral("Error calling method on NPObject.")));
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
203 JSValue 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, ASCIILiteral("Error calling method on NPObject.")));
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
240 bool CInstance::supportsConstruct() const
241 {
242     return _object->_class->construct;
243 }
244
245 JSValue 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, ASCIILiteral("Error calling method on NPObject.")));
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
282 JSValue 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
291 JSValue 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, ASCIILiteral("NPObject"));
299 }
300
301 JSValue CInstance::numberValue(ExecState*) const
302 {
303     // FIXME: Implement something sensible.
304     return jsNumber(0);
305 }
306
307 JSValue CInstance::booleanValue() const
308 {
309     // As per ECMA 9.2.
310     return jsBoolean(getObject());
311 }
312
313 JSValue 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
323 bool 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, ASCIILiteral("Error calling method on NPObject.")));
346
347     resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
348     _NPN_ReleaseVariantValue(&resultVariant);
349     return true;
350 }
351
352 void 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)