Reviewed by Adam.
[WebKit-https.git] / WebKit / win / WebScriptCallFrame.cpp
1 /*
2  * Copyright (C) 2007 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "WebKitDLL.h"
31 #include "WebScriptCallFrame.h"
32
33 #include "IWebScriptScope.h"
34 #include "Function.h"
35 #include "WebScriptScope.h"
36
37 #include <JavaScriptCore/JSGlobalObject.h>
38 #include <JavaScriptCore/JSStringRefBSTR.h>
39 #include <JavaScriptCore/JSValueRef.h>
40
41 #pragma warning(push, 0)
42 #include <WebCore/BString.h>
43 #include <WebCore/PlatformString.h>
44 #pragma warning(pop)
45
46 #include <wtf/Assertions.h>
47
48 using namespace KJS;
49
50 // EnumScopes -----------------------------------------------------------------
51
52 class EnumScopes : public IEnumVARIANT {
53 public:
54     static EnumScopes* createInstance(ScopeChain chain);
55
56 private:
57     EnumScopes(ScopeChain chain)
58         : m_refCount(0)
59         , m_chain(chain)
60         , m_current(chain.begin())
61     {
62     }
63
64 public:
65     virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void** ppvObject);
66     virtual ULONG STDMETHODCALLTYPE AddRef();
67     virtual ULONG STDMETHODCALLTYPE Release();
68     virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, VARIANT* rgVar, ULONG* pCeltFetched);
69     virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
70     virtual HRESULT STDMETHODCALLTYPE Reset(void);
71     virtual HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT**);
72
73 private:
74     ULONG m_refCount;
75     ScopeChain m_chain;
76     ScopeChainIterator m_current;
77 };
78
79 EnumScopes* EnumScopes::createInstance(ScopeChain chain)
80 {
81     EnumScopes* instance = new EnumScopes(chain);
82     instance->AddRef();
83     return instance;
84 }
85
86 HRESULT STDMETHODCALLTYPE EnumScopes::QueryInterface(REFIID riid, void** ppvObject)
87 {
88     *ppvObject = 0;
89     if (IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IEnumVARIANT))
90         *ppvObject = this;
91     else
92         return E_NOINTERFACE;
93
94     AddRef();
95     return S_OK;
96 }
97
98 ULONG STDMETHODCALLTYPE EnumScopes::AddRef()
99 {
100     return ++m_refCount;
101 }
102
103 ULONG STDMETHODCALLTYPE EnumScopes::Release()
104 {
105     ULONG newRef = --m_refCount;
106     if (!newRef)
107         delete(this);
108     return newRef;
109 }
110
111 HRESULT STDMETHODCALLTYPE EnumScopes::Next(ULONG celt, VARIANT* rgVar, ULONG* pCeltFetched)
112 {
113     if (pCeltFetched)
114         *pCeltFetched = 0;
115     if (!rgVar)
116         return E_POINTER;
117     VariantInit(rgVar);
118     if (!celt || celt > 1)
119         return S_FALSE;
120     if (m_current == m_chain.end())
121         return S_FALSE;
122
123     // Create a WebScriptScope from the m_current then put it in an IUnknown.
124     COMPtr<IWebScriptScope> scope(AdoptCOM, WebScriptScope::createInstance(*m_current));
125     ++m_current;
126     if (!scope)
127         return E_FAIL;
128
129     V_VT(rgVar) = VT_UNKNOWN;
130     V_UNKNOWN(rgVar) = scope.releaseRef();
131
132     if (pCeltFetched)
133         *pCeltFetched = 1;
134     return S_OK;
135 }
136
137 HRESULT STDMETHODCALLTYPE EnumScopes::Skip(ULONG celt)
138 {
139     for (ULONG i = 0; i < celt; ++i)
140         ++m_current;
141     return (m_current != m_chain.end()) ? S_OK : S_FALSE;
142 }
143
144 HRESULT STDMETHODCALLTYPE EnumScopes::Reset(void)
145 {
146     m_current = m_chain.begin();
147     return S_OK;
148 }
149
150 HRESULT STDMETHODCALLTYPE EnumScopes::Clone(IEnumVARIANT**)
151 {
152     return E_NOTIMPL;
153 }
154
155 // WebScriptCallFrame -----------------------------------------------------------
156
157 WebScriptCallFrame::WebScriptCallFrame(ExecState* state, IWebScriptCallFrame* caller)
158     : m_refCount(0)
159 {
160     m_state = state;
161     m_caller = caller;
162
163     gClassCount++;
164 }
165
166 WebScriptCallFrame::~WebScriptCallFrame()
167 {
168     gClassCount--;
169 }
170
171 WebScriptCallFrame* WebScriptCallFrame::createInstance(ExecState* state, IWebScriptCallFrame* caller)
172 {
173     WebScriptCallFrame* instance = new WebScriptCallFrame(state, caller);
174     instance->AddRef();
175     return instance;
176 }
177
178 // IUnknown ------------------------------------------------------------------
179
180 HRESULT STDMETHODCALLTYPE WebScriptCallFrame::QueryInterface(REFIID riid, void** ppvObject)
181 {
182     *ppvObject = 0;
183     if (IsEqualGUID(riid, IID_IUnknown))
184         *ppvObject = static_cast<IWebScriptCallFrame*>(this);
185     else if (IsEqualGUID(riid, IID_IWebScriptCallFrame))
186         *ppvObject = static_cast<IWebScriptCallFrame*>(this);
187     else
188         return E_NOINTERFACE;
189
190     AddRef();
191     return S_OK;
192 }
193
194 ULONG STDMETHODCALLTYPE WebScriptCallFrame::AddRef()
195 {
196     return ++m_refCount;
197 }
198
199 ULONG STDMETHODCALLTYPE WebScriptCallFrame::Release()
200 {
201     ULONG newRef = --m_refCount;
202     if (!newRef)
203         delete(this);
204
205     return newRef;
206 }
207
208 // IWebScriptCallFrame -----------------------------------------------------------
209
210 HRESULT STDMETHODCALLTYPE WebScriptCallFrame::caller(
211     /* [out, retval] */ IWebScriptCallFrame** callFrame)
212 {
213     return m_caller.copyRefTo(callFrame);
214 }
215
216 HRESULT STDMETHODCALLTYPE WebScriptCallFrame::scopeChain(
217     /* [out, retval] */ IEnumVARIANT** result)
218 {
219     if (!result)
220         return E_POINTER;
221
222     // FIXME: If there is no current body do we need to make scope chain from the global object?
223
224     *result = EnumScopes::createInstance(m_state->scopeChain());
225
226     return S_OK;
227 }
228
229 HRESULT STDMETHODCALLTYPE WebScriptCallFrame::functionName(
230     /* [out, retval] */ BSTR* funcName)
231 {
232     if (!funcName)
233         return E_POINTER;
234
235     *funcName = 0;
236
237     if (!m_state->currentBody())
238         return S_OK;
239
240     FunctionImp* func = m_state->function();
241     if (!func)
242         return E_FAIL;
243
244     const Identifier& funcIdent = func->functionName();
245     if (!funcIdent.isEmpty())
246         *funcName = WebCore::BString(funcIdent).release();
247
248     return S_OK;
249 }
250
251 HRESULT STDMETHODCALLTYPE WebScriptCallFrame::stringByEvaluatingJavaScriptFromString(
252     /* [in] */ BSTR script,
253     /* [out, retval] */ BSTR* result)
254 {
255     if (!script)
256         return E_FAIL;
257
258     if (!result)
259         return E_POINTER;
260
261     *result = 0;
262
263     JSLock lock;
264
265     JSValue* scriptExecutionResult = valueByEvaluatingJavaScriptFromString(script);
266
267     if (scriptExecutionResult && scriptExecutionResult->isString())
268         *result = WebCore::BString(WebCore::String(scriptExecutionResult->getString())).release();
269     else
270         return E_FAIL;
271
272     return S_OK;
273 }
274
275 JSValue* WebScriptCallFrame::valueByEvaluatingJavaScriptFromString(BSTR script)
276 {
277     ExecState* state = m_state;
278     Interpreter* interp  = state->dynamicInterpreter();
279     JSObject* globObj = interp->globalObject();
280
281     // find "eval"
282     JSObject* eval = 0;
283     if (state->currentBody()) {  // "eval" won't work without context (i.e. at global scope)
284         JSValue* v = globObj->get(state, "eval");
285         if (v->isObject() && static_cast<JSObject*>(v)->implementsCall())
286             eval = static_cast<JSObject*>(v);
287         else
288             // no "eval" - fallback operates on global exec state
289             state = interp->globalExec();
290     }
291
292     JSValue* savedException = state->exception();
293     state->clearException();
294
295     UString code(reinterpret_cast<KJS::UChar*>(script), SysStringLen(script));
296
297     // evaluate
298     JSValue* scriptExecutionResult;
299     if (eval) {
300         List args;
301         args.append(jsString(code));
302         scriptExecutionResult = eval->call(state, 0, args);
303     } else
304         // no "eval", or no context (i.e. global scope) - use global fallback
305         scriptExecutionResult = interp->evaluate(UString(), 0, code.data(), code.size(), globObj).value();
306
307     if (state->hadException())
308         scriptExecutionResult = state->exception();    // (may be redundant depending on which eval path was used)
309     state->setException(savedException);
310
311     return scriptExecutionResult;
312 }
313