1 /*
2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "NPRuntimeObjectMap.h"
28
29 #include "JSNPObject.h"
30 #include "NPJSObject.h"
31 #include "NPRuntimeUtilities.h"
32 #include "PluginView.h"
33 #include <JavaScriptCore/Error.h>
34 #include <JavaScriptCore/JSLock.h>
35 #include <JavaScriptCore/SourceCode.h>
36 #include <JavaScriptCore/Strong.h>
37 #include <WebCore/Frame.h>
38 #include <WebCore/NotImplemented.h>
39
40 using namespace JSC;
41 using namespace WebCore;
42
43 namespace WebKit {
44
45
NPRuntimeObjectMap(PluginView * pluginView)46 NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView)
47 : m_pluginView(pluginView)
48 {
49 }
50
PluginProtector(NPRuntimeObjectMap * npRuntimeObjectMap)51 NPRuntimeObjectMap::PluginProtector::PluginProtector(NPRuntimeObjectMap* npRuntimeObjectMap)
52 {
53 // If we're already in the plug-in view destructor, we shouldn't try to keep it alive.
54 if (!npRuntimeObjectMap->m_pluginView->isBeingDestroyed())
55 m_pluginView = npRuntimeObjectMap->m_pluginView;
56 }
57
~PluginProtector()58 NPRuntimeObjectMap::PluginProtector::~PluginProtector()
59 {
60 }
61
getOrCreateNPObject(JSGlobalData & globalData,JSObject * jsObject)62 NPObject* NPRuntimeObjectMap::getOrCreateNPObject(JSGlobalData& globalData, JSObject* jsObject)
63 {
64 // If this is a JSNPObject, we can just get its underlying NPObject.
65 if (jsObject->classInfo() == &JSNPObject::s_info) {
66 JSNPObject* jsNPObject = static_cast<JSNPObject*>(jsObject);
67 NPObject* npObject = jsNPObject->npObject();
68
69 retainNPObject(npObject);
70 return npObject;
71 }
72
73 // First, check if we already know about this object.
74 if (NPJSObject* npJSObject = m_npJSObjects.get(jsObject)) {
75 retainNPObject(npJSObject);
76 return npJSObject;
77 }
78
79 NPJSObject* npJSObject = NPJSObject::create(globalData, this, jsObject);
80 m_npJSObjects.set(jsObject, npJSObject);
81
82 return npJSObject;
83 }
84
npJSObjectDestroyed(NPJSObject * npJSObject)85 void NPRuntimeObjectMap::npJSObjectDestroyed(NPJSObject* npJSObject)
86 {
87 // Remove the object from the map.
88 ASSERT(m_npJSObjects.contains(npJSObject->jsObject()));
89 m_npJSObjects.remove(npJSObject->jsObject());
90 }
91
getOrCreateJSObject(JSGlobalObject * globalObject,NPObject * npObject)92 JSObject* NPRuntimeObjectMap::getOrCreateJSObject(JSGlobalObject* globalObject, NPObject* npObject)
93 {
94 // If this is an NPJSObject, we can just get the JSObject that it's wrapping.
95 if (NPJSObject::isNPJSObject(npObject))
96 return NPJSObject::toNPJSObject(npObject)->jsObject();
97
98 if (JSNPObject* jsNPObject = m_jsNPObjects.get(npObject))
99 return jsNPObject;
100
101 JSNPObject* jsNPObject = new (&globalObject->globalData()) JSNPObject(globalObject, this, npObject);
102 m_jsNPObjects.set(npObject, jsNPObject);
103
104 return jsNPObject;
105 }
106
jsNPObjectDestroyed(JSNPObject * jsNPObject)107 void NPRuntimeObjectMap::jsNPObjectDestroyed(JSNPObject* jsNPObject)
108 {
109 // Remove the object from the map.
110 ASSERT(m_jsNPObjects.contains(jsNPObject->npObject()));
111 m_jsNPObjects.remove(jsNPObject->npObject());
112 }
113
convertNPVariantToJSValue(JSC::ExecState * exec,JSC::JSGlobalObject * globalObject,const NPVariant & variant)114 JSValue NPRuntimeObjectMap::convertNPVariantToJSValue(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const NPVariant& variant)
115 {
116 switch (variant.type) {
117 case NPVariantType_Void:
118 return jsUndefined();
119
120 case NPVariantType_Null:
121 return jsNull();
122
123 case NPVariantType_Bool:
124 return jsBoolean(variant.value.boolValue);
125
126 case NPVariantType_Int32:
127 return jsNumber(variant.value.intValue);
128
129 case NPVariantType_Double:
130 return jsNumber(variant.value.doubleValue);
131
132 case NPVariantType_String:
133 return jsString(exec, String::fromUTF8WithLatin1Fallback(variant.value.stringValue.UTF8Characters,
134 variant.value.stringValue.UTF8Length));
135 case NPVariantType_Object:
136 return getOrCreateJSObject(globalObject, variant.value.objectValue);
137 }
138
139 ASSERT_NOT_REACHED();
140 return jsUndefined();
141 }
142
convertJSValueToNPVariant(ExecState * exec,JSValue value,NPVariant & variant)143 void NPRuntimeObjectMap::convertJSValueToNPVariant(ExecState* exec, JSValue value, NPVariant& variant)
144 {
145 JSLock lock(SilenceAssertionsOnly);
146
147 VOID_TO_NPVARIANT(variant);
148
149 if (value.isNull()) {
150 NULL_TO_NPVARIANT(variant);
151 return;
152 }
153
154 if (value.isUndefined()) {
155 VOID_TO_NPVARIANT(variant);
156 return;
157 }
158
159 if (value.isBoolean()) {
160 BOOLEAN_TO_NPVARIANT(value.toBoolean(exec), variant);
161 return;
162 }
163
164 if (value.isNumber()) {
165 DOUBLE_TO_NPVARIANT(value.toNumber(exec), variant);
166 return;
167 }
168
169 if (value.isString()) {
170 NPString npString = createNPString(value.toString(exec).utf8());
171 STRINGN_TO_NPVARIANT(npString.UTF8Characters, npString.UTF8Length, variant);
172 return;
173 }
174
175 if (value.isObject()) {
176 NPObject* npObject = getOrCreateNPObject(exec->globalData(), asObject(value));
177 OBJECT_TO_NPVARIANT(npObject, variant);
178 return;
179 }
180
181 ASSERT_NOT_REACHED();
182 }
183
evaluate(NPObject * npObject,const String & scriptString,NPVariant * result)184 bool NPRuntimeObjectMap::evaluate(NPObject* npObject, const String&scriptString, NPVariant* result)
185 {
186 Strong<JSGlobalObject> globalObject(this->globalObject()->globalData(), this->globalObject());
187 if (!globalObject)
188 return false;
189
190 ExecState* exec = globalObject->globalExec();
191
192 JSLock lock(SilenceAssertionsOnly);
193 JSValue thisValue = getOrCreateJSObject(globalObject.get(), npObject);
194
195 globalObject->globalData().timeoutChecker.start();
196 Completion completion = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(UString(scriptString.impl())), thisValue);
197 globalObject->globalData().timeoutChecker.stop();
198
199 ComplType completionType = completion.complType();
200
201 JSValue resultValue;
202 if (completionType == Normal) {
203 resultValue = completion.value();
204 if (!resultValue)
205 resultValue = jsUndefined();
206 } else
207 resultValue = jsUndefined();
208
209 exec->clearException();
210
211 convertJSValueToNPVariant(exec, resultValue, *result);
212 return true;
213 }
214
invalidate()215 void NPRuntimeObjectMap::invalidate()
216 {
217 Vector<NPJSObject*> npJSObjects;
218 copyValuesToVector(m_npJSObjects, npJSObjects);
219
220 // Deallocate all the object wrappers so we won't leak any JavaScript objects.
221 for (size_t i = 0; i < npJSObjects.size(); ++i)
222 deallocateNPObject(npJSObjects[i]);
223
224 // We shouldn't have any NPJSObjects left now.
225 ASSERT(m_npJSObjects.isEmpty());
226
227 Vector<JSNPObject*> jsNPObjects;
228 copyValuesToVector(m_jsNPObjects, jsNPObjects);
229
230 // Invalidate all the JSObjects that wrap NPObjects.
231 for (size_t i = 0; i < jsNPObjects.size(); ++i)
232 jsNPObjects[i]->invalidate();
233
234 m_jsNPObjects.clear();
235 }
236
globalObject() const237 JSGlobalObject* NPRuntimeObjectMap::globalObject() const
238 {
239 Frame* frame = m_pluginView->frame();
240 if (!frame)
241 return 0;
242
243 return frame->script()->globalObject(pluginWorld());
244 }
245
globalExec() const246 ExecState* NPRuntimeObjectMap::globalExec() const
247 {
248 JSGlobalObject* globalObject = this->globalObject();
249 if (!globalObject)
250 return 0;
251
252 return globalObject->globalExec();
253 }
254
globalExceptionString()255 static String& globalExceptionString()
256 {
257 DEFINE_STATIC_LOCAL(String, exceptionString, ());
258 return exceptionString;
259 }
260
setGlobalException(const String & exceptionString)261 void NPRuntimeObjectMap::setGlobalException(const String& exceptionString)
262 {
263 globalExceptionString() = exceptionString;
264 }
265
moveGlobalExceptionToExecState(ExecState * exec)266 void NPRuntimeObjectMap::moveGlobalExceptionToExecState(ExecState* exec)
267 {
268 if (globalExceptionString().isNull())
269 return;
270
271 {
272 JSLock lock(SilenceAssertionsOnly);
273 throwError(exec, createError(exec, stringToUString(globalExceptionString())));
274 }
275
276 globalExceptionString() = String();
277 }
278
279 } // namespace WebKit
280