| /* |
| * Copyright (C) 2007 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "IWebScriptDebugListener.h" |
| #include "WebKitDLL.h" |
| #include "WebScriptDebugServer.h" |
| |
| #include "WebView.h" |
| #include <wtf/Assertions.h> |
| #include <wtf/Vector.h> |
| |
| typedef HashSet<COMPtr<IWebScriptDebugListener> > ListenerSet; |
| |
| static ListenerSet s_Listeners; |
| static unsigned s_ListenerCount = 0; |
| static OwnPtr<WebScriptDebugServer> s_SharedWebScriptDebugServer; |
| static bool s_dying = false; |
| |
| unsigned WebScriptDebugServer::listenerCount() { return s_ListenerCount; }; |
| |
| // WebScriptDebugServer ------------------------------------------------------------ |
| |
| WebScriptDebugServer::WebScriptDebugServer() |
| : m_refCount(0) |
| , m_paused(false) |
| , m_step(false) |
| { |
| gClassCount++; |
| } |
| |
| WebScriptDebugServer::~WebScriptDebugServer() |
| { |
| gClassCount--; |
| } |
| |
| WebScriptDebugServer* WebScriptDebugServer::createInstance() |
| { |
| WebScriptDebugServer* instance = new WebScriptDebugServer; |
| instance->AddRef(); |
| return instance; |
| } |
| |
| WebScriptDebugServer* WebScriptDebugServer::sharedWebScriptDebugServer() |
| { |
| if (!s_SharedWebScriptDebugServer) { |
| s_dying = false; |
| s_SharedWebScriptDebugServer.set(WebScriptDebugServer::createInstance()); |
| } |
| |
| return s_SharedWebScriptDebugServer.get(); |
| } |
| |
| |
| // IUnknown ------------------------------------------------------------------- |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::QueryInterface(REFIID riid, void** ppvObject) |
| { |
| *ppvObject = 0; |
| if (IsEqualGUID(riid, IID_IUnknown)) |
| *ppvObject = static_cast<WebScriptDebugServer*>(this); |
| else if (IsEqualGUID(riid, IID_IWebScriptDebugServer)) |
| *ppvObject = static_cast<WebScriptDebugServer*>(this); |
| else |
| return E_NOINTERFACE; |
| |
| AddRef(); |
| return S_OK; |
| } |
| |
| ULONG STDMETHODCALLTYPE WebScriptDebugServer::AddRef() |
| { |
| return ++m_refCount; |
| } |
| |
| ULONG STDMETHODCALLTYPE WebScriptDebugServer::Release() |
| { |
| ULONG newRef = --m_refCount; |
| if (!newRef) |
| delete(this); |
| |
| return newRef; |
| } |
| |
| // IWebScriptDebugServer ----------------------------------------------------------- |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::sharedWebScriptDebugServer( |
| /* [retval][out] */ IWebScriptDebugServer** server) |
| { |
| if (!server) |
| return E_POINTER; |
| |
| *server = WebScriptDebugServer::sharedWebScriptDebugServer(); |
| (*server)->AddRef(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::addListener( |
| /* [in] */ IWebScriptDebugListener* listener) |
| { |
| if (s_dying) |
| return E_FAIL; |
| |
| if (!listener) |
| return E_POINTER; |
| |
| ++s_ListenerCount; |
| s_Listeners.add(listener); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::removeListener( |
| /* [in] */ IWebScriptDebugListener* listener) |
| { |
| if (s_dying) |
| return S_OK; |
| |
| if (!listener) |
| return E_POINTER; |
| |
| if (!s_Listeners.contains(listener)) |
| return S_OK; |
| |
| s_Listeners.remove(listener); |
| |
| ASSERT(s_ListenerCount >= 1); |
| if (--s_ListenerCount == 0) |
| resume(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::step() |
| { |
| m_step = true; |
| m_paused = false; |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::pause() |
| { |
| m_paused = true; |
| m_step = false; |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::resume() |
| { |
| m_paused = false; |
| m_step = false; |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::isPaused( |
| /* [out, retval] */ BOOL* isPaused) |
| { |
| if (!isPaused) |
| return E_POINTER; |
| |
| *isPaused = m_paused; |
| return S_OK; |
| } |
| |
| |
| void WebScriptDebugServer::suspendProcessIfPaused() |
| { |
| static bool alreadyHere = false; |
| |
| if (alreadyHere) |
| return; |
| |
| alreadyHere = true; |
| |
| MSG msg; |
| while (m_paused && GetMessage(&msg, 0, 0, 0)) { |
| TranslateMessage(&msg); |
| DispatchMessage(&msg); |
| } |
| |
| if (m_step) { |
| m_step = false; |
| m_paused = true; |
| } |
| |
| alreadyHere = false; |
| } |
| |
| // IWebScriptDebugListener |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::didLoadMainResourceForDataSource( |
| /* [in] */ IWebView* webView, |
| /* [in] */ IWebDataSource* dataSource) |
| { |
| if (!webView || !dataSource) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).didLoadMainResourceForDataSource(webView, dataSource); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::didParseSource( |
| /* [in] */ IWebView* webView, |
| /* [in] */ BSTR sourceCode, |
| /* [in] */ UINT baseLineNumber, |
| /* [in] */ BSTR url, |
| /* [in] */ int sourceID, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !sourceCode || !url || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).didParseSource(webView, sourceCode, baseLineNumber, url, sourceID, webFrame); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::failedToParseSource( |
| /* [in] */ IWebView* webView, |
| /* [in] */ BSTR sourceCode, |
| /* [in] */ UINT baseLineNumber, |
| /* [in] */ BSTR url, |
| /* [in] */ BSTR error, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !sourceCode || !url || !error || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).failedToParseSource(webView, sourceCode, baseLineNumber, url, error, webFrame); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::didEnterCallFrame( |
| /* [in] */ IWebView* webView, |
| /* [in] */ IWebScriptCallFrame* frame, |
| /* [in] */ int sourceID, |
| /* [in] */ int lineNumber, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !frame || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).didEnterCallFrame(webView, frame, sourceID, lineNumber, webFrame); |
| |
| suspendProcessIfPaused(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::willExecuteStatement( |
| /* [in] */ IWebView* webView, |
| /* [in] */ IWebScriptCallFrame* frame, |
| /* [in] */ int sourceID, |
| /* [in] */ int lineNumber, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !frame || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).willExecuteStatement(webView, frame, sourceID, lineNumber, webFrame); |
| |
| suspendProcessIfPaused(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::willLeaveCallFrame( |
| /* [in] */ IWebView* webView, |
| /* [in] */ IWebScriptCallFrame* frame, |
| /* [in] */ int sourceID, |
| /* [in] */ int lineNumber, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !frame || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).willLeaveCallFrame(webView, frame, sourceID, lineNumber, webFrame); |
| |
| suspendProcessIfPaused(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::exceptionWasRaised( |
| /* [in] */ IWebView* webView, |
| /* [in] */ IWebScriptCallFrame* frame, |
| /* [in] */ int sourceID, |
| /* [in] */ int lineNumber, |
| /* [in] */ IWebFrame* webFrame) |
| { |
| if (!webView || !frame || !webFrame) |
| return E_FAIL; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) |
| (**it).exceptionWasRaised(webView, frame, sourceID, lineNumber, webFrame); |
| |
| suspendProcessIfPaused(); |
| |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE WebScriptDebugServer::serverDidDie() |
| { |
| s_dying = true; |
| |
| ListenerSet listenersCopy = s_Listeners; |
| ListenerSet::iterator end = listenersCopy.end(); |
| for (ListenerSet::iterator it = listenersCopy.begin(); it != end; ++it) { |
| (**it).serverDidDie(); |
| s_Listeners.remove((*it).get()); |
| } |
| |
| return S_OK; |
| } |