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
40using namespace JSC;
41using namespace WebCore;
42
43namespace WebKit {
44
45
46NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView)
47    : m_pluginView(pluginView)
48{
49}
50
51NPRuntimeObjectMap::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
58NPRuntimeObjectMap::PluginProtector::~PluginProtector()
59{
60}
61
62NPObject* 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
85void 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
92JSObject* 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
107void 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
114JSValue 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
143void 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
184bool 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
215void 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
237JSGlobalObject* 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
246ExecState* NPRuntimeObjectMap::globalExec() const
247{
248    JSGlobalObject* globalObject = this->globalObject();
249    if (!globalObject)
250        return 0;
251
252    return globalObject->globalExec();
253}
254
255static String& globalExceptionString()
256{
257    DEFINE_STATIC_LOCAL(String, exceptionString, ());
258    return exceptionString;
259}
260
261void NPRuntimeObjectMap::setGlobalException(const String& exceptionString)
262{
263    globalExceptionString() = exceptionString;
264}
265
266void 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