Propagate the source origin as much as possible
[WebKit-https.git] / Source / WebCore / bridge / NP_jsobject.cpp
1 /*
2  * Copyright (C) 2004, 2006 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 "NP_jsobject.h"
31
32 #include "c_utility.h"
33 #include "c_instance.h"
34 #include "IdentifierRep.h"
35 #include "JSDOMBinding.h"
36 #include "npruntime_priv.h"
37 #include "runtime_root.h"
38 #include <runtime/Error.h>
39 #include <runtime/JSGlobalObject.h>
40 #include <runtime/JSLock.h>
41 #include <runtime/PropertyNameArray.h>
42 #include <parser/SourceCode.h>
43 #include <runtime/Completion.h>
44 #include <wtf/NeverDestroyed.h>
45 #include <wtf/text/WTFString.h>
46
47 #pragma GCC visibility push(default)
48 #include "npruntime_impl.h"
49 #pragma GCC visibility pop
50
51 using namespace JSC;
52 using namespace JSC::Bindings;
53 using namespace WebCore;
54
55 class ObjectMap {
56 public:
57     NPObject* get(RootObject* rootObject, JSObject* jsObject)
58     {
59         return m_map.get(rootObject).get(jsObject);
60     }
61
62     void add(RootObject* rootObject, JSObject* jsObject, NPObject* npObject)
63     {
64         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
65         if (iter == m_map.end()) {
66             rootObject->addInvalidationCallback(&m_invalidationCallback);
67             iter = m_map.add(rootObject, JSToNPObjectMap()).iterator;
68         }
69
70         ASSERT(iter->value.find(jsObject) == iter->value.end());
71         iter->value.add(jsObject, npObject);
72     }
73
74     void remove(RootObject* rootObject)
75     {
76         ASSERT(m_map.contains(rootObject));
77         m_map.remove(rootObject);
78     }
79
80     void remove(RootObject* rootObject, JSObject* jsObject)
81     {
82         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
83         ASSERT(iter != m_map.end());
84         ASSERT(iter->value.find(jsObject) != iter->value.end());
85
86         iter->value.remove(jsObject);
87     }
88
89 private:
90     struct RootObjectInvalidationCallback : public RootObject::InvalidationCallback {
91         void operator()(RootObject*) override;
92     };
93     RootObjectInvalidationCallback m_invalidationCallback;
94
95     // JSObjects are protected by RootObject.
96     typedef HashMap<JSObject*, NPObject*> JSToNPObjectMap;
97     HashMap<RootObject*, JSToNPObjectMap> m_map;
98 };
99
100
101 static ObjectMap& objectMap()
102 {
103     static NeverDestroyed<ObjectMap> map;
104     return map;
105 }
106
107 void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject)
108 {
109     objectMap().remove(rootObject);
110 }
111
112 static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, MarkedArgumentBuffer& aList)
113 {
114     for (unsigned i = 0; i < argCount; ++i)
115         aList.append(convertNPVariantToValue(exec, &args[i], rootObject));
116 }
117
118 static NPObject* jsAllocate(NPP, NPClass*)
119 {
120     return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject)));
121 }
122
123 static void jsDeallocate(NPObject* npObj)
124 {
125     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(npObj);
126
127     if (obj->rootObject && obj->rootObject->isValid()) {
128         objectMap().remove(obj->rootObject, obj->imp);
129         obj->rootObject->gcUnprotect(obj->imp);
130     }
131
132     if (obj->rootObject)
133         obj->rootObject->deref();
134
135     free(obj);
136 }
137
138 static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
139 static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
140
141 NPClass* NPScriptObjectClass = &javascriptClass;
142 static NPClass* NPNoScriptObjectClass = &noScriptClass;
143
144 NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, RefPtr<RootObject>&& rootObject)
145 {
146     if (NPObject* object = objectMap().get(rootObject.get(), imp))
147         return _NPN_RetainObject(object);
148
149     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass));
150
151     obj->rootObject = rootObject.leakRef();
152
153     if (obj->rootObject) {
154         obj->rootObject->gcProtect(imp);
155         objectMap().add(obj->rootObject, imp, reinterpret_cast<NPObject*>(obj));
156     }
157
158     obj->imp = imp;
159
160     return reinterpret_cast<NPObject*>(obj);
161 }
162
163 NPObject* _NPN_CreateNoScriptObject(void)
164 {
165     return _NPN_CreateObject(0, NPNoScriptObjectClass);
166 }
167
168 bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
169 {
170     if (o->_class == NPScriptObjectClass) {
171         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
172         
173         VOID_TO_NPVARIANT(*result);
174         
175         // Lookup the function object.
176         RootObject* rootObject = obj->rootObject;
177         if (!rootObject || !rootObject->isValid())
178             return false;
179         
180         auto globalObject = rootObject->globalObject();
181         VM& vm = globalObject->vm();
182         JSLockHolder lock(vm);
183         auto scope = DECLARE_CATCH_SCOPE(vm);
184
185         ExecState* exec = globalObject->globalExec();
186         
187         // Call the function object.
188         JSValue function = obj->imp;
189         CallData callData;
190         CallType callType = getCallData(function, callData);
191         if (callType == CallType::None)
192             return false;
193         
194         MarkedArgumentBuffer argList;
195         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
196         JSValue resultV = JSC::call(exec, function, callType, callData, function, argList);
197
198         // Convert and return the result of the function call.
199         convertValueToNPVariant(exec, resultV, result);
200         scope.clearException();
201         return true;        
202     }
203
204     if (o->_class->invokeDefault)
205         return o->_class->invokeDefault(o, args, argCount, result);    
206     VOID_TO_NPVARIANT(*result);
207     return true;
208 }
209
210 bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
211 {
212     if (o->_class == NPScriptObjectClass) {
213         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
214
215         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
216         if (!i->isString())
217             return false;
218
219         // Special case the "eval" method.
220         if (methodName == _NPN_GetStringIdentifier("eval")) {
221             if (argCount != 1)
222                 return false;
223             if (args[0].type != NPVariantType_String)
224                 return false;
225             return _NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result);
226         }
227
228         // Look up the function object.
229         RootObject* rootObject = obj->rootObject;
230         if (!rootObject || !rootObject->isValid())
231             return false;
232
233         auto globalObject = rootObject->globalObject();
234         VM& vm = globalObject->vm();
235         JSLockHolder lock(vm);
236         auto scope = DECLARE_CATCH_SCOPE(vm);
237
238         ExecState* exec = globalObject->globalExec();
239         JSValue function = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
240         CallData callData;
241         CallType callType = getCallData(function, callData);
242         if (callType == CallType::None)
243             return false;
244
245         // Call the function object.
246         MarkedArgumentBuffer argList;
247         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
248         JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp, argList);
249
250         // Convert and return the result of the function call.
251         convertValueToNPVariant(exec, resultV, result);
252         scope.clearException();
253         return true;
254     }
255
256     if (o->_class->invoke)
257         return o->_class->invoke(o, methodName, args, argCount, result);
258     
259     VOID_TO_NPVARIANT(*result);
260     return true;
261 }
262
263 bool _NPN_Evaluate(NPP, NPObject* o, NPString* s, NPVariant* variant)
264 {
265     if (o->_class == NPScriptObjectClass) {
266         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
267
268         RootObject* rootObject = obj->rootObject;
269         if (!rootObject || !rootObject->isValid())
270             return false;
271
272         auto globalObject = rootObject->globalObject();
273         VM& vm = globalObject->vm();
274         JSLockHolder lock(vm);
275         auto scope = DECLARE_CATCH_SCOPE(vm);
276
277         ExecState* exec = globalObject->globalExec();
278         String scriptString = convertNPStringToUTF16(s);
279         
280         JSValue returnValue = JSC::evaluate(exec, JSC::makeSource(scriptString, { }), JSC::JSValue());
281
282         convertValueToNPVariant(exec, returnValue, variant);
283         scope.clearException();
284         return true;
285     }
286
287     VOID_TO_NPVARIANT(*variant);
288     return false;
289 }
290
291 bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant)
292 {
293     if (o->_class == NPScriptObjectClass) {
294         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
295
296         RootObject* rootObject = obj->rootObject;
297         if (!rootObject || !rootObject->isValid())
298             return false;
299
300         auto globalObject = rootObject->globalObject();
301         VM& vm = globalObject->vm();
302         JSLockHolder lock(vm);
303         auto scope = DECLARE_CATCH_SCOPE(vm);
304
305         ExecState* exec = globalObject->globalExec();
306         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
307         
308         JSValue result;
309         if (i->isString())
310             result = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
311         else
312             result = obj->imp->get(exec, i->number());
313
314         convertValueToNPVariant(exec, result, variant);
315         scope.clearException();
316         return true;
317     }
318
319     if (o->_class->hasProperty && o->_class->getProperty) {
320         if (o->_class->hasProperty(o, propertyName))
321             return o->_class->getProperty(o, propertyName, variant);
322         return false;
323     }
324
325     VOID_TO_NPVARIANT(*variant);
326     return false;
327 }
328
329 bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant)
330 {
331     if (o->_class == NPScriptObjectClass) {
332         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
333
334         RootObject* rootObject = obj->rootObject;
335         if (!rootObject || !rootObject->isValid())
336             return false;
337
338         auto globalObject = rootObject->globalObject();
339         VM& vm = globalObject->vm();
340         JSLockHolder lock(vm);
341         auto scope = DECLARE_CATCH_SCOPE(vm);
342
343         ExecState* exec = globalObject->globalExec();
344         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
345
346         if (i->isString()) {
347             PutPropertySlot slot(obj->imp);
348             obj->imp->methodTable()->put(obj->imp, exec, identifierFromNPIdentifier(exec, i->string()), convertNPVariantToValue(exec, variant, rootObject), slot);
349         } else
350             obj->imp->methodTable()->putByIndex(obj->imp, exec, i->number(), convertNPVariantToValue(exec, variant, rootObject), false);
351         scope.clearException();
352         return true;
353     }
354
355     if (o->_class->setProperty)
356         return o->_class->setProperty(o, propertyName, variant);
357
358     return false;
359 }
360
361 bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName)
362 {
363     if (o->_class == NPScriptObjectClass) {
364         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
365
366         RootObject* rootObject = obj->rootObject;
367         if (!rootObject || !rootObject->isValid())
368             return false;
369
370         auto globalObject = rootObject->globalObject();
371         VM& vm = globalObject->vm();
372         JSLockHolder lock(vm);
373         auto scope = DECLARE_CATCH_SCOPE(vm);
374
375         ExecState* exec = globalObject->globalExec();
376
377         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
378         if (i->isString()) {
379             if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()))) {
380                 scope.clearException();
381                 return false;
382             }
383         } else {
384             if (!obj->imp->hasProperty(exec, i->number())) {
385                 scope.clearException();
386                 return false;
387             }
388         }
389
390         if (i->isString())
391             obj->imp->methodTable()->deleteProperty(obj->imp, exec, identifierFromNPIdentifier(exec, i->string()));
392         else
393             obj->imp->methodTable()->deletePropertyByIndex(obj->imp, exec, i->number());
394
395         scope.clearException();
396         return true;
397     }
398     return false;
399 }
400
401 bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName)
402 {
403     if (o->_class == NPScriptObjectClass) {
404         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
405
406         RootObject* rootObject = obj->rootObject;
407         if (!rootObject || !rootObject->isValid())
408             return false;
409
410         auto globalObject = rootObject->globalObject();
411         VM& vm = globalObject->vm();
412         JSLockHolder lock(vm);
413         auto scope = DECLARE_CATCH_SCOPE(vm);
414
415         ExecState* exec = globalObject->globalExec();
416         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
417         if (i->isString()) {
418             bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()));
419             scope.clearException();
420             return result;
421         }
422
423         bool result = obj->imp->hasProperty(exec, i->number());
424         scope.clearException();
425         return result;
426     }
427
428     if (o->_class->hasProperty)
429         return o->_class->hasProperty(o, propertyName);
430
431     return false;
432 }
433
434 bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName)
435 {
436     if (o->_class == NPScriptObjectClass) {
437         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
438
439         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
440         if (!i->isString())
441             return false;
442
443         RootObject* rootObject = obj->rootObject;
444         if (!rootObject || !rootObject->isValid())
445             return false;
446
447         auto globalObject = rootObject->globalObject();
448         VM& vm = globalObject->vm();
449         JSLockHolder lock(vm);
450         auto scope = DECLARE_CATCH_SCOPE(vm);
451
452         ExecState* exec = globalObject->globalExec();
453         JSValue func = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
454         scope.clearException();
455         return !func.isUndefined();
456     }
457     
458     if (o->_class->hasMethod)
459         return o->_class->hasMethod(o, methodName);
460     
461     return false;
462 }
463
464 void _NPN_SetException(NPObject*, const NPUTF8* message)
465 {
466     // Ignoring the NPObject param is consistent with the Mozilla implementation.
467     String exception(message);
468     CInstance::setGlobalException(exception);
469 }
470
471 bool _NPN_Enumerate(NPP, NPObject* o, NPIdentifier** identifier, uint32_t* count)
472 {
473     if (o->_class == NPScriptObjectClass) {
474         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
475         
476         RootObject* rootObject = obj->rootObject;
477         if (!rootObject || !rootObject->isValid())
478             return false;
479         
480         auto globalObject = rootObject->globalObject();
481         VM& vm = globalObject->vm();
482         JSLockHolder lock(vm);
483         auto scope = DECLARE_CATCH_SCOPE(vm);
484
485         ExecState* exec = globalObject->globalExec();
486         PropertyNameArray propertyNames(exec, PropertyNameMode::Strings);
487
488         obj->imp->methodTable()->getPropertyNames(obj->imp, exec, propertyNames, EnumerationMode());
489         unsigned size = static_cast<unsigned>(propertyNames.size());
490         // FIXME: This should really call NPN_MemAlloc but that's in WebKit
491         NPIdentifier* identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size));
492         
493         for (unsigned i = 0; i < size; ++i)
494             identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].string().utf8().data());
495
496         *identifier = identifiers;
497         *count = size;
498
499         scope.clearException();
500         return true;
501     }
502     
503     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate)
504         return o->_class->enumerate(o, identifier, count);
505     
506     return false;
507 }
508
509 bool _NPN_Construct(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
510 {
511     if (o->_class == NPScriptObjectClass) {
512         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
513         
514         VOID_TO_NPVARIANT(*result);
515         
516         // Lookup the constructor object.
517         RootObject* rootObject = obj->rootObject;
518         if (!rootObject || !rootObject->isValid())
519             return false;
520
521         auto globalObject = rootObject->globalObject();
522         VM& vm = globalObject->vm();
523         JSLockHolder lock(vm);
524         auto scope = DECLARE_CATCH_SCOPE(vm);
525
526         ExecState* exec = globalObject->globalExec();
527         
528         // Call the constructor object.
529         JSValue constructor = obj->imp;
530         ConstructData constructData;
531         ConstructType constructType = getConstructData(constructor, constructData);
532         if (constructType == ConstructType::None)
533             return false;
534         
535         MarkedArgumentBuffer argList;
536         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
537         JSValue resultV = JSC::construct(exec, constructor, constructType, constructData, argList);
538         
539         // Convert and return the result.
540         convertValueToNPVariant(exec, resultV, result);
541         scope.clearException();
542         return true;
543     }
544     
545     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(o->_class) && o->_class->construct)
546         return o->_class->construct(o, args, argCount, result);
547     
548     return false;
549 }
550
551 #endif // ENABLE(NETSCAPE_PLUGIN_API)