1/*
2 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include "config.h"
21#include "JSPluginElementFunctions.h"
22
23#include "BridgeJSC.h"
24#include "HTMLNames.h"
25#include "HTMLPlugInElement.h"
26#include "JSHTMLElement.h"
27#include "PluginViewBase.h"
28
29using namespace JSC;
30
31namespace WebCore {
32
33using namespace Bindings;
34using namespace HTMLNames;
35
36// JavaScript access to plug-in-exported properties for JSHTMLAppletElement, JSHTMLEmbedElement and JSHTMLObjectElement.
37
38static inline bool isPluginElement(HTMLElement& element)
39{
40    return element.hasTagName(objectTag) || element.hasTagName(embedTag) || element.hasTagName(appletTag);
41}
42
43Instance* pluginInstance(HTMLElement& element)
44{
45    // The plugin element holds an owning reference, so we don't have to.
46    if (!isPluginElement(element))
47        return 0;
48    Instance* instance = toHTMLPlugInElement(element).getInstance().get();
49    if (!instance || !instance->rootObject())
50        return 0;
51    return instance;
52}
53
54static JSObject* pluginScriptObjectFromPluginViewBase(HTMLPlugInElement& pluginElement, JSGlobalObject* globalObject)
55{
56    Widget* pluginWidget = pluginElement.pluginWidget();
57    if (!pluginWidget)
58        return 0;
59
60    if (!pluginWidget->isPluginViewBase())
61        return 0;
62
63    PluginViewBase* pluginViewBase = toPluginViewBase(pluginWidget);
64    return pluginViewBase->scriptObject(globalObject);
65}
66
67static JSObject* pluginScriptObjectFromPluginViewBase(JSHTMLElement* jsHTMLElement)
68{
69    HTMLElement& element = jsHTMLElement->impl();
70    if (!isPluginElement(element))
71        return 0;
72
73    HTMLPlugInElement& pluginElement = toHTMLPlugInElement(element);
74    return pluginScriptObjectFromPluginViewBase(pluginElement, jsHTMLElement->globalObject());
75}
76
77JSObject* pluginScriptObject(ExecState* exec, JSHTMLElement* jsHTMLElement)
78{
79    HTMLElement& element = jsHTMLElement->impl();
80    if (!isPluginElement(element))
81        return 0;
82
83    HTMLPlugInElement& pluginElement = toHTMLPlugInElement(element);
84
85    // First, see if the element has a plug-in replacement with a script.
86    if (JSObject* scriptObject = pluginElement.scriptObjectForPluginReplacement())
87        return scriptObject;
88
89    // Next, see if we can ask the plug-in view for its script object.
90    if (JSObject* scriptObject = pluginScriptObjectFromPluginViewBase(pluginElement, jsHTMLElement->globalObject()))
91        return scriptObject;
92
93    // Otherwise, fall back to getting the object from the instance.
94
95    // The plugin element holds an owning reference, so we don't have to.
96    Instance* instance = pluginElement.getInstance().get();
97    if (!instance || !instance->rootObject())
98        return 0;
99
100    return instance->createRuntimeObject(exec);
101}
102
103EncodedJSValue pluginElementPropertyGetter(ExecState* exec, JSObject*, EncodedJSValue thisValue, PropertyName propertyName)
104{
105
106    JSHTMLElement* thisObject = jsDynamicCast<JSHTMLElement*>(JSValue::decode(thisValue));
107    if (!thisObject)
108        return throwVMTypeError(exec);
109    JSObject* scriptObject = pluginScriptObject(exec, thisObject);
110    if (!scriptObject)
111        return JSValue::encode(jsUndefined());
112
113    return JSValue::encode(scriptObject->get(exec, propertyName));
114}
115
116bool pluginElementCustomGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot, JSHTMLElement* element)
117{
118    JSObject* scriptObject = pluginScriptObject(exec, element);
119    if (!scriptObject)
120        return false;
121
122    if (!scriptObject->hasProperty(exec, propertyName))
123        return false;
124    slot.setCustom(element, DontDelete | DontEnum, pluginElementPropertyGetter);
125    return true;
126}
127
128bool pluginElementCustomPut(ExecState* exec, PropertyName propertyName, JSValue value, JSHTMLElement* element, PutPropertySlot& slot)
129{
130    JSObject* scriptObject = pluginScriptObject(exec, element);
131    if (!scriptObject)
132        return 0;
133    if (!scriptObject->hasProperty(exec, propertyName))
134        return false;
135    scriptObject->methodTable()->put(scriptObject, exec, propertyName, value, slot);
136    return true;
137}
138
139static EncodedJSValue JSC_HOST_CALL callPlugin(ExecState* exec)
140{
141    JSHTMLElement* element = jsCast<JSHTMLElement*>(exec->callee());
142
143    // Get the plug-in script object.
144    JSObject* scriptObject = pluginScriptObject(exec, element);
145    ASSERT(scriptObject);
146
147    size_t argumentCount = exec->argumentCount();
148    MarkedArgumentBuffer argumentList;
149    for (size_t i = 0; i < argumentCount; i++)
150        argumentList.append(exec->argument(i));
151
152    CallData callData;
153    CallType callType = getCallData(scriptObject, callData);
154    ASSERT(callType == CallTypeHost);
155
156    // Call the object.
157    JSValue result = call(exec, scriptObject, callType, callData, exec->thisValue(), argumentList);
158    return JSValue::encode(result);
159}
160
161CallType pluginElementGetCallData(JSHTMLElement* element, CallData& callData)
162{
163    // First, ask the plug-in view base for its runtime object.
164    if (JSObject* scriptObject = pluginScriptObjectFromPluginViewBase(element)) {
165        CallData scriptObjectCallData;
166
167        if (scriptObject->methodTable()->getCallData(scriptObject, scriptObjectCallData) == CallTypeNone)
168            return CallTypeNone;
169
170        callData.native.function = callPlugin;
171        return CallTypeHost;
172    }
173
174    Instance* instance = pluginInstance(element->impl());
175    if (!instance || !instance->supportsInvokeDefaultMethod())
176        return CallTypeNone;
177    callData.native.function = callPlugin;
178    return CallTypeHost;
179}
180
181} // namespace WebCore
182