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