/* * 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "Drosera.h" #include "DebuggerClient.h" #include "DebuggerDocument.h" #include "resource.h" #include "ServerConnection.h" #include #include #include #include #include const unsigned MAX_LOADSTRING = 100; TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name static const LRESULT kNotHandledResult = -1; static LPCTSTR kDroseraPointerProp = TEXT("DroseraPointer"); static HINSTANCE hInst; BSTR cfStringToBSTR(CFStringRef cfstr); void registerDroseraClass(HINSTANCE hInstance); LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); HINSTANCE Drosera::getInst() { return hInst; } void Drosera::setInst(HINSTANCE in) { hInst = in; } void launchConsoleWindow(); extern "C" __declspec(dllimport) HANDLE* __pioinfo; int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); MSG msg; #ifndef NDEBUG launchConsoleWindow(); #endif Drosera drosera; HRESULT ret = drosera.init(hInstance, nCmdShow); if (FAILED(ret)) return ret; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DROSERA)); // Main message loop: while (GetMessage(&msg, 0, 0, 0)) { if (!drosera.serverConnected()) drosera.attemptToCreateServerConnection(); if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return static_cast(msg.wParam); } void launchConsoleWindow() { if (AllocConsole()) { // MSVCRT exports __pioinfo which is an array of ioinfo handles. the first three are stdout, stdin, and stderr // the first pointer in the ioinfo object is the kernel handle for the console, so we can simplify the expression // to just deref the exported symbol, setting it to the newly allocated console handle. *__pioinfo = GetStdHandle(STD_OUTPUT_HANDLE); // When an app is created without a console, stdout, stderr and stdin are all invalid handles (i.e. negative) // Since we've introduced new handles, we can reset their file index - which is the index into the ioinfo array. // This hooks up the standard cruntime APIS to the new console, allowing a functional output. As for input YMMV. stdout->_file = 0; stderr->_file = 0; } } ////////////////// Setup Windows Specific Interface ////////////////// void registerDroseraClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = ::droseraWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(Drosera*); wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DROSERA)); wcex.hCursor = LoadCursor(0, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_DROSERA); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); RegisterClassEx(&wcex); } //Processes messages for the main window. LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0); Drosera* drosera = reinterpret_cast(longPtr); switch (message) { case WM_COMMAND: return drosera->handleCommand(hWnd, message, wParam, lParam); break; case WM_SIZE: if (!drosera) return 0; return drosera->webViewLoaded() ? drosera->onSize(wParam, lParam) : 0; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } LRESULT CALLBACK Drosera::handleCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId = LOWORD(wParam); switch (wmId) { case ID_DEBUG_CONTINUE: m_debuggerClient->resume(); break; case ID_DEBUG_PAUSE: m_debuggerClient->pause(); break; case ID_DEBUG_STEPINTO: m_debuggerClient->stepInto(); break; case ID_DEBUG_STEPOVER: m_debuggerClient->stepOver(); break; case ID_DEBUG_STEPOUT: m_debuggerClient->stepOut(); break; case ID_DEBUG_SHOWCONSOLE: m_debuggerClient->showConsole(); break; case ID_HELP_ABOUT: DialogBox(Drosera::getInst(), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, ::aboutWndProc); break; case ID_FILE_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Message handler for about box. INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } ////////////////// End Setup Windows Specific Interface ////////////////// Drosera::Drosera() : m_hWnd(0) , m_debuggerClient(new DebuggerClient()) { } HRESULT Drosera::init(HINSTANCE hInstance, int nCmdShow) { HRESULT ret = initUI(hInstance, nCmdShow); if (FAILED(ret)) return ret; ret = attach(); return ret; } HRESULT Drosera::initUI(HINSTANCE hInstance, int nCmdShow) { // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle)); LoadString(hInstance, IDC_DROSERA, szWindowClass, ARRAYSIZE(szWindowClass)); registerDroseraClass(hInstance); Drosera::setInst(hInstance); // Store instance handle in our local variable m_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0); if (!m_hWnd) return HRESULT_FROM_WIN32(GetLastError()); SetLastError(0); SetWindowLongPtr(m_hWnd, 0, reinterpret_cast(this)); HRESULT ret = HRESULT_FROM_WIN32(GetLastError()); if (FAILED(ret)) return ret; ret = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&m_webView); if (FAILED(ret)) return ret; m_webViewPrivate.query(m_webView.get()); if (!m_webViewPrivate) return E_FAIL; ret = m_webView->setHostWindow(reinterpret_cast(m_hWnd)); if (FAILED(ret)) return ret; RECT clientRect = {0}; ::GetClientRect(m_hWnd, &clientRect); ret = m_webView->initWithFrame(clientRect, 0, 0); if (FAILED(ret)) return ret; HWND viewWindow; ret = m_webViewPrivate->viewWindow(reinterpret_cast(&viewWindow)); if (FAILED(ret)) return ret; ::SetProp(viewWindow, kDroseraPointerProp, (HANDLE)this); // FIXME: Implement window size/position save/restore ShowWindow(m_hWnd, nCmdShow); UpdateWindow(m_hWnd); return ret; } LRESULT Drosera::onSize(WPARAM, LPARAM) { if (!m_webViewPrivate) return 0; RECT clientRect = {0}; ::GetClientRect(m_hWnd, &clientRect); HWND viewWindow; if (SUCCEEDED(m_webViewPrivate->viewWindow(reinterpret_cast(&viewWindow)))) // FIXME should this be the height-command bars height? ::SetWindowPos(viewWindow, 0, clientRect.left, clientRect.top, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_NOZORDER); return 0; } bool Drosera::webViewLoaded() const { return m_debuggerClient->webViewLoaded(); } // Server Detection Callbacks HRESULT Drosera::attach() { // Get selected server HRESULT ret = m_webView->setFrameLoadDelegate(m_debuggerClient.get()); if (FAILED(ret)) return ret; ret = m_webView->setUIDelegate(m_debuggerClient.get()); if (FAILED(ret)) return ret; COMPtr request; ret = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request); if (FAILED(ret)) return ret; RetainPtr htmlURLRef(AdoptCF, ::CFBundleCopyResourceURL(::CFBundleGetBundleWithIdentifier(CFSTR("org.webkit.drosera")), CFSTR("debugger"), CFSTR("html"), CFSTR("Drosera"))); if (!htmlURLRef) return E_FAIL; CFStringRef urlStringRef = ::CFURLGetString(htmlURLRef.get()); BSTR tempStr = cfStringToBSTR(urlStringRef); // Both initWithRUL and SysFreeString can handle 0. ret = request->initWithURL(tempStr, WebURLRequestUseProtocolCachePolicy, 60); SysFreeString(tempStr); if (FAILED(ret)) return ret; COMPtr mainFrame; ret = m_webView->mainFrame(&mainFrame); if (FAILED(ret)) return ret; ret = mainFrame->loadRequest(request.get()); if (FAILED(ret)) return ret; return ret; } BSTR cfStringToBSTR(CFStringRef cfstr) { if (!cfstr) return 0; const UniChar* uniChars = CFStringGetCharactersPtr(cfstr); if (uniChars) return SysAllocStringLen((LPCTSTR)uniChars, CFStringGetLength(cfstr)); CFIndex length = CFStringGetLength(cfstr); BSTR bstr = SysAllocStringLen(0, length); CFStringGetCharacters(cfstr, CFRangeMake(0, length), (UniChar*)bstr); bstr[length] = 0; return bstr; } // Server Connection Functions bool Drosera::serverConnected() const { return m_debuggerClient->serverConnected(); } void Drosera::attemptToCreateServerConnection() { m_debuggerClient->attemptToCreateServerConnection(); }