Remove WebScriptDebugger
[WebKit-https.git] / WebKit / win / WebScriptDebugServer.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  * 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 COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "WebKit.h"
28 #include "WebKitDLL.h"
29 #include "WebScriptDebugServer.h"
30
31 #include "WebScriptCallFrame.h"
32 #include "WebView.h"
33 #pragma warning(push, 0)
34 #include <WebCore/DOMWindow.h>
35 #include <WebCore/JSDOMWindow.h>
36 #include <WebCore/Page.h>
37 #pragma warning(pop)
38 #include <kjs/ExecState.h>
39 #include <wtf/Assertions.h>
40 #include <wtf/Vector.h>
41
42 using namespace KJS;
43 using namespace WebCore;
44
45 typedef HashSet<COMPtr<IWebScriptDebugListener> > ListenerSet;
46
47 static ListenerSet s_Listeners;
48 static unsigned s_ListenerCount = 0;
49 static OwnPtr<WebScriptDebugServer> s_SharedWebScriptDebugServer;
50 static bool s_dying = false;
51
52 static Frame* frame(ExecState* exec)
53 {
54     JSDOMWindow* window = static_cast<JSDOMWindow*>(exec->dynamicGlobalObject());
55     return window->impl()->frame();
56 }
57
58 static WebFrame* webFrame(ExecState* exec)
59 {
60     return kit(frame(exec));
61 }
62
63 static WebView* webView(ExecState* exec)
64 {
65     return kit(frame(exec)->page());
66 }
67
68 unsigned WebScriptDebugServer::listenerCount() { return s_ListenerCount; };
69
70 // WebScriptDebugServer ------------------------------------------------------------
71
72 WebScriptDebugServer::WebScriptDebugServer()
73     : m_refCount(0)
74     , m_paused(false)
75     , m_step(false)
76     , m_callingServer(false)
77 {
78     gClassCount++;
79 }
80
81 WebScriptDebugServer::~WebScriptDebugServer()
82 {
83     gClassCount--;
84 }
85
86 WebScriptDebugServer* WebScriptDebugServer::createInstance()
87 {
88     WebScriptDebugServer* instance = new WebScriptDebugServer;
89     instance->AddRef();
90     return instance;
91 }
92
93 WebScriptDebugServer* WebScriptDebugServer::sharedWebScriptDebugServer()
94 {
95     if (!s_SharedWebScriptDebugServer) {
96         s_dying = false;
97         s_SharedWebScriptDebugServer.set(WebScriptDebugServer::createInstance());
98     }
99
100     return s_SharedWebScriptDebugServer.get();
101 }
102
103 void WebScriptDebugServer::pageCreated(Page* page)
104 {
105     ASSERT_ARG(page, page);
106
107     if (s_ListenerCount > 0)
108         page->setDebugger(sharedWebScriptDebugServer());
109 }
110
111 // IUnknown -------------------------------------------------------------------
112
113 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::QueryInterface(REFIID riid, void** ppvObject)
114 {
115     *ppvObject = 0;
116     if (IsEqualGUID(riid, IID_IUnknown))
117         *ppvObject = static_cast<WebScriptDebugServer*>(this);
118     else if (IsEqualGUID(riid, IID_IWebScriptDebugServer))
119         *ppvObject = static_cast<WebScriptDebugServer*>(this);
120     else
121         return E_NOINTERFACE;
122
123     AddRef();
124     return S_OK;
125 }
126
127 ULONG STDMETHODCALLTYPE WebScriptDebugServer::AddRef()
128 {
129     return ++m_refCount;
130 }
131
132 ULONG STDMETHODCALLTYPE WebScriptDebugServer::Release()
133 {
134     ULONG newRef = --m_refCount;
135     if (!newRef)
136         delete(this);
137
138     return newRef;
139 }
140
141 // IWebScriptDebugServer -----------------------------------------------------------
142
143 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::sharedWebScriptDebugServer( 
144     /* [retval][out] */ IWebScriptDebugServer** server)
145 {
146     if (!server)
147         return E_POINTER;
148
149     *server = WebScriptDebugServer::sharedWebScriptDebugServer();
150     (*server)->AddRef();
151
152     return S_OK;
153 }
154
155 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::addListener(
156     /* [in] */ IWebScriptDebugListener* listener)
157 {
158     if (s_dying)
159         return E_FAIL;
160
161     if (!listener)
162         return E_POINTER;
163
164     if (!s_ListenerCount)
165         Page::setDebuggerForAllPages(sharedWebScriptDebugServer());
166
167     ++s_ListenerCount;
168     s_Listeners.add(listener);
169
170     return S_OK;
171 }
172
173 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::removeListener(
174     /* [in] */ IWebScriptDebugListener* listener)
175 {
176     if (s_dying)
177         return S_OK;
178
179     if (!listener)
180         return E_POINTER;
181
182     if (!s_Listeners.contains(listener))
183         return S_OK;
184
185     s_Listeners.remove(listener);
186
187     ASSERT(s_ListenerCount >= 1);
188     if (--s_ListenerCount == 0) {
189         Page::setDebuggerForAllPages(0);
190         resume();
191     }
192
193     return S_OK;
194 }
195
196 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::step()
197 {
198     m_step = true;
199     m_paused = false;
200
201     return S_OK;
202 }
203
204 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::pause()
205 {
206     m_paused = true;
207     m_step = false;
208
209     return S_OK;
210 }
211
212 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::resume()
213 {
214     m_paused = false;
215     m_step = false;
216
217     return S_OK;
218 }
219
220 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::isPaused(
221     /* [out, retval] */ BOOL* isPaused)
222 {
223     if (!isPaused)
224         return E_POINTER;
225
226     *isPaused = m_paused;
227     return S_OK;
228 }
229
230 static HWND comMessageWindow()
231 {
232     static bool initialized;
233     static HWND window;
234
235     if (initialized)
236         return window;
237     initialized = true;
238
239     static LPCTSTR windowClass = TEXT("OleMainThreadWndClass");
240     static LPCTSTR windowText = TEXT("OleMainThreadWndName");
241     static const DWORD currentProcess = GetCurrentProcessId();
242
243     window = 0;
244     DWORD windowProcess = 0;
245     do {
246         window = FindWindowEx(HWND_MESSAGE, window, windowClass, windowText);
247         GetWindowThreadProcessId(window, &windowProcess);
248     } while (window && windowProcess != currentProcess);
249
250     return window;
251 }
252
253 void WebScriptDebugServer::suspendProcessIfPaused()
254 {
255     static bool alreadyHere = false;
256
257     if (alreadyHere)
258         return;
259
260     alreadyHere = true;
261
262     // We only deliver messages to COM's message window to pause the process while still allowing RPC to work.
263     // FIXME: It would be nice if we could keep delivering WM_[NC]PAINT messages to all windows to keep them painting on XP.
264
265     HWND messageWindow = comMessageWindow();
266
267     MSG msg;
268     while (m_paused && GetMessage(&msg, messageWindow, 0, 0)) {
269         TranslateMessage(&msg);
270         DispatchMessage(&msg);
271     }
272
273     if (m_step) {
274         m_step = false;
275         m_paused = true;
276     }
277
278     alreadyHere = false;
279 }
280
281 // IWebScriptDebugListener
282 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::didLoadMainResourceForDataSource(
283     /* [in] */ IWebView* webView,
284     /* [in] */ IWebDataSource* dataSource)
285 {
286     if (!webView || !dataSource)
287         return E_FAIL;
288
289     ListenerSet listenersCopy = s_Listeners;
290     ListenerSet::iterator end = listenersCopy.end();
291     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
292         (**it).didLoadMainResourceForDataSource(webView, dataSource);
293
294     return S_OK;
295 }
296
297 bool WebScriptDebugServer::sourceParsed(ExecState* exec, int sourceID, const UString& sourceURL,
298                   const UString& source, int startingLineNumber, int errorLine, const UString& /*errorMsg*/)
299 {
300     if (m_callingServer)
301         return true;
302
303     m_callingServer = true;
304
305     if (listenerCount() <= 0)
306         return true;
307
308     BString bSource = String(source);
309     BString bSourceURL = String(sourceURL);
310     
311     ListenerSet listenersCopy = s_Listeners;
312     ListenerSet::iterator end = listenersCopy.end();
313     if (errorLine == -1) {
314         for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
315             (**it).didParseSource(webView(exec), bSource, startingLineNumber, bSourceURL, sourceID, webFrame(exec));
316     } else {
317         // FIXME: the error var should be made with the information in the errorMsg.  It is not a simple
318         // UString to BSTR conversion there is some logic involved that I don't fully understand yet.
319         BString error(L"An Error Occurred.");
320         for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
321             (**it).failedToParseSource(webView(exec), bSource, startingLineNumber, bSourceURL, error, webFrame(exec));
322     }
323
324     m_callingServer = false;
325     return true;
326 }
327
328 bool WebScriptDebugServer::callEvent(ExecState* exec, int sourceID, int lineNumber, JSObject* /*function*/, const List &/*args*/)
329 {
330     if (m_callingServer)
331         return true;
332
333     m_callingServer = true;
334
335     COMPtr<WebScriptCallFrame> callFrame(AdoptCOM, WebScriptCallFrame::createInstance(exec));
336     ListenerSet listenersCopy = s_Listeners;
337     ListenerSet::iterator end = listenersCopy.end();
338     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
339         (**it).didEnterCallFrame(webView(exec), callFrame.get(), sourceID, lineNumber, webFrame(exec));
340
341     suspendProcessIfPaused();
342
343     m_callingServer = false;
344
345     return true;
346 }
347
348 bool WebScriptDebugServer::atStatement(ExecState* exec, int sourceID, int firstLine, int /*lastLine*/)
349 {
350     if (m_callingServer)
351         return true;
352
353     m_callingServer = true;
354
355     COMPtr<WebScriptCallFrame> callFrame(AdoptCOM, WebScriptCallFrame::createInstance(exec));
356     ListenerSet listenersCopy = s_Listeners;
357     ListenerSet::iterator end = listenersCopy.end();
358     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
359         (**it).willExecuteStatement(webView(exec), callFrame.get(), sourceID, firstLine, webFrame(exec));
360
361     suspendProcessIfPaused();
362
363     m_callingServer = false;
364
365     return true;
366 }
367
368 bool WebScriptDebugServer::returnEvent(ExecState* exec, int sourceID, int lineNumber, JSObject* /*function*/)
369 {
370     if (m_callingServer)
371         return true;
372
373     m_callingServer = true;
374
375     COMPtr<WebScriptCallFrame> callFrame(AdoptCOM, WebScriptCallFrame::createInstance(exec->callingExecState()));
376     ListenerSet listenersCopy = s_Listeners;
377     ListenerSet::iterator end = listenersCopy.end();
378     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
379         (**it).willLeaveCallFrame(webView(exec), callFrame.get(), sourceID, lineNumber, webFrame(exec));
380
381     suspendProcessIfPaused();
382
383     m_callingServer = false;
384
385     return true;
386 }
387
388 bool WebScriptDebugServer::exception(ExecState* exec, int sourceID, int lineNumber, JSValue* /*exception */)
389 {
390     if (m_callingServer)
391         return true;
392
393     m_callingServer = true;
394
395     COMPtr<WebScriptCallFrame> callFrame(AdoptCOM, WebScriptCallFrame::createInstance(exec));
396     ListenerSet listenersCopy = s_Listeners;
397     ListenerSet::iterator end = listenersCopy.end();
398     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it)
399         (**it).exceptionWasRaised(webView(exec), callFrame.get(), sourceID, lineNumber, webFrame(exec));
400
401     suspendProcessIfPaused();
402
403     m_callingServer = false;
404
405     return true;
406 }
407
408 HRESULT STDMETHODCALLTYPE WebScriptDebugServer::serverDidDie()
409 {
410     s_dying = true;
411
412     ListenerSet listenersCopy = s_Listeners;
413     ListenerSet::iterator end = listenersCopy.end();
414     for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) {
415         (**it).serverDidDie();
416         s_Listeners.remove((*it).get());
417     }
418
419     return S_OK;
420 }