1/*
2 * Copyright (C) 2008 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#import "WebScriptDebugger.h"
30
31#import "WebDelegateImplementationCaching.h"
32#import "WebFrameInternal.h"
33#import "WebScriptDebugDelegate.h"
34#import "WebViewInternal.h"
35#import <JavaScriptCore/DebuggerCallFrame.h>
36#import <JavaScriptCore/JSGlobalObject.h>
37#import <JavaScriptCore/SourceProvider.h>
38#import <WebCore/DOMWindow.h>
39#import <WebCore/Frame.h>
40#import <WebCore/JSDOMWindow.h>
41#import <WebCore/KURL.h>
42#import <WebCore/ScriptController.h>
43
44using namespace JSC;
45using namespace WebCore;
46
47@interface WebScriptCallFrame (WebScriptDebugDelegateInternal)
48- (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj debugger:(WebScriptDebugger *)debugger caller:(WebScriptCallFrame *)caller debuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame;
49- (void)_setDebuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame;
50- (void)_clearDebuggerCallFrame;
51@end
52
53NSString *toNSString(const UString& s)
54{
55    if (s.isEmpty())
56        return nil;
57    return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(s.characters()) length:s.length()];
58}
59
60static NSString *toNSString(SourceProvider* s)
61{
62    if (!s->length())
63        return nil;
64    return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(s->data()) length:s->length()];
65}
66
67// convert UString to NSURL
68static NSURL *toNSURL(const UString& s)
69{
70    if (s.isEmpty())
71        return nil;
72    return KURL(ParsedURLString, ustringToString(s));
73}
74
75static WebFrame *toWebFrame(JSGlobalObject* globalObject)
76{
77    JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject);
78    return kit(window->impl()->frame());
79}
80
81WebScriptDebugger::WebScriptDebugger(JSGlobalObject* globalObject)
82    : m_callingDelegate(false)
83    , m_globalObject(globalObject->globalData(), globalObject)
84{
85    attach(globalObject);
86    initGlobalCallFrame(globalObject->globalExec());
87}
88
89void WebScriptDebugger::initGlobalCallFrame(const DebuggerCallFrame& debuggerCallFrame)
90{
91    m_callingDelegate = true;
92
93    WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject());
94
95    m_topCallFrame.adoptNS([[WebScriptCallFrame alloc] _initWithGlobalObject:core(webFrame)->script()->windowScriptObject() debugger:this caller:m_topCallFrame.get() debuggerCallFrame:debuggerCallFrame]);
96    m_globalCallFrame = m_topCallFrame;
97
98    WebView *webView = [webFrame webView];
99    WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView);
100    if (implementations->didEnterCallFrameFunc)
101        CallScriptDebugDelegate(implementations->didEnterCallFrameFunc, webView, @selector(webView:didEnterCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), static_cast<NSInteger>(0), -1, webFrame);
102
103    m_callingDelegate = false;
104}
105
106// callbacks - relay to delegate
107void WebScriptDebugger::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const UString& errorMsg)
108{
109    if (m_callingDelegate)
110        return;
111
112    m_callingDelegate = true;
113
114    NSString *nsSource = toNSString(sourceProvider);
115    NSURL *nsURL = toNSURL(sourceProvider->url());
116    int firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
117
118    WebFrame *webFrame = toWebFrame(exec->dynamicGlobalObject());
119    WebView *webView = [webFrame webView];
120    WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView);
121
122    if (errorLine == -1) {
123        if (implementations->didParseSourceFunc) {
124            if (implementations->didParseSourceExpectsBaseLineNumber)
125                CallScriptDebugDelegate(implementations->didParseSourceFunc, webView, @selector(webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:), nsSource, firstLine, nsURL, sourceProvider->asID(), webFrame);
126            else
127                CallScriptDebugDelegate(implementations->didParseSourceFunc, webView, @selector(webView:didParseSource:fromURL:sourceId:forWebFrame:), nsSource, [nsURL absoluteString], sourceProvider->asID(), webFrame);
128        }
129    } else {
130        NSString* nsErrorMessage = toNSString(errorMsg);
131        NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:nsErrorMessage, WebScriptErrorDescriptionKey, [NSNumber numberWithUnsignedInt:errorLine], WebScriptErrorLineNumberKey, nil];
132        NSError *error = [[NSError alloc] initWithDomain:WebScriptErrorDomain code:WebScriptGeneralErrorCode userInfo:info];
133
134        if (implementations->failedToParseSourceFunc)
135            CallScriptDebugDelegate(implementations->failedToParseSourceFunc, webView, @selector(webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:), nsSource, firstLine, nsURL, error, webFrame);
136
137        [error release];
138        [info release];
139    }
140
141    m_callingDelegate = false;
142}
143
144void WebScriptDebugger::callEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
145{
146    if (m_callingDelegate)
147        return;
148
149    m_callingDelegate = true;
150
151    WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject());
152
153    m_topCallFrame.adoptNS([[WebScriptCallFrame alloc] _initWithGlobalObject:core(webFrame)->script()->windowScriptObject() debugger:this caller:m_topCallFrame.get() debuggerCallFrame:debuggerCallFrame]);
154
155    WebView *webView = [webFrame webView];
156    WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView);
157    if (implementations->didEnterCallFrameFunc)
158        CallScriptDebugDelegate(implementations->didEnterCallFrameFunc, webView, @selector(webView:didEnterCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame);
159
160    m_callingDelegate = false;
161}
162
163void WebScriptDebugger::atStatement(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
164{
165    if (m_callingDelegate)
166        return;
167
168    m_callingDelegate = true;
169
170    WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject());
171    WebView *webView = [webFrame webView];
172
173    [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame];
174
175    WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView);
176    if (implementations->willExecuteStatementFunc)
177        CallScriptDebugDelegate(implementations->willExecuteStatementFunc, webView, @selector(webView:willExecuteStatement:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame);
178
179    m_callingDelegate = false;
180}
181
182void WebScriptDebugger::returnEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
183{
184    if (m_callingDelegate)
185        return;
186
187    m_callingDelegate = true;
188
189    WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject());
190    WebView *webView = [webFrame webView];
191
192    [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame];
193
194    WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView);
195    if (implementations->willLeaveCallFrameFunc)
196        CallScriptDebugDelegate(implementations->willLeaveCallFrameFunc, webView, @selector(webView:willLeaveCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame);
197
198    [m_topCallFrame.get() _clearDebuggerCallFrame];
199    m_topCallFrame = [m_topCallFrame.get() caller];
200
201    m_callingDelegate = false;
202}
203
204void WebScriptDebugger::exception(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber, bool hasHandler)
205{
206    if (m_callingDelegate)
207        return;
208
209    m_callingDelegate = true;
210
211    WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject());
212    WebView *webView = [webFrame webView];
213    [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame];
214
215    WebScriptDebugDelegateImplementationCache* cache = WebViewGetScriptDebugDelegateImplementations(webView);
216    if (cache->exceptionWasRaisedFunc) {
217        if (cache->exceptionWasRaisedExpectsHasHandlerFlag)
218            CallScriptDebugDelegate(cache->exceptionWasRaisedFunc, webView, @selector(webView:exceptionWasRaised:hasHandler:sourceId:line:forWebFrame:), m_topCallFrame.get(), hasHandler, sourceID, lineNumber, webFrame);
219        else
220            CallScriptDebugDelegate(cache->exceptionWasRaisedFunc, webView, @selector(webView:exceptionWasRaised:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame);
221    }
222
223    m_callingDelegate = false;
224}
225
226void WebScriptDebugger::willExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineno)
227{
228    callEvent(debuggerCallFrame, sourceID, lineno);
229}
230
231void WebScriptDebugger::didExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineno)
232{
233    returnEvent(debuggerCallFrame, sourceID, lineno);
234}
235
236void WebScriptDebugger::didReachBreakpoint(const DebuggerCallFrame&, intptr_t, int)
237{
238    return;
239}
240