Windows build fixes
[WebKit-https.git] / WebKitTools / Drosera / win / Drosera.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  *
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 #include "config.h"
30 #include "Drosera.h"
31
32 #include "DebuggerClient.h"
33 #include "DebuggerDocument.h"
34 #include "resource.h"
35 #include "ServerConnection.h"
36
37 #include <JavaScriptCore/JSStringRef.h>
38 #include <WebKit/ForEachCoClass.h>
39 #include <WebKit/WebKit.h>
40 #include <wtf/RetainPtr.h>
41
42 const unsigned MAX_LOADSTRING = 100;
43
44 TCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
45 TCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
46
47 static LPCTSTR s_DroseraPointerProp = TEXT("DroseraPointer");
48 static HINSTANCE hInst;
49
50 BSTR cfStringToBSTR(CFStringRef cfstr);
51
52 void registerDroseraClass(HINSTANCE hInstance);
53 LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
54 INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
55
56 HINSTANCE Drosera::getInst() { return hInst; }
57 void Drosera::setInst(HINSTANCE in) { hInst = in; }
58 void launchConsoleWindow();
59
60 extern "C" __declspec(dllimport) HANDLE* __pioinfo;
61
62 int APIENTRY _tWinMain(HINSTANCE hInstance,
63                        HINSTANCE hPrevInstance,
64                        LPTSTR    lpCmdLine,
65                        int       nCmdShow)
66 {
67     UNREFERENCED_PARAMETER(hPrevInstance);
68     UNREFERENCED_PARAMETER(lpCmdLine);
69
70     MSG msg;
71
72 #ifndef NDEBUG
73     launchConsoleWindow();
74 #endif
75
76     Drosera drosera;
77
78     HRESULT ret = drosera.init(hInstance, nCmdShow);
79     if (FAILED(ret))
80         return ret;
81
82     HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DROSERA));
83
84     // Main message loop:
85     while (GetMessage(&msg, 0, 0, 0)) {
86         if (!drosera.serverConnected())
87             drosera.attemptToCreateServerConnection();
88
89         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
90             TranslateMessage(&msg);
91             DispatchMessage(&msg);
92         }
93     }
94
95     return static_cast<int>(msg.wParam);
96 }
97
98 void launchConsoleWindow()
99 {
100     if (AllocConsole()) {
101         // MSVCRT exports __pioinfo which is an array of ioinfo handles. the first three are stdout, stdin, and stderr
102         // the first pointer in the ioinfo object is the kernel handle for the console, so we can simplify the expression
103         // to just deref the exported symbol, setting it to the newly allocated console handle.
104         *__pioinfo = GetStdHandle(STD_OUTPUT_HANDLE);
105         // When an app is created without a console, stdout, stderr and stdin are all invalid handles (i.e. negative)
106         // Since we've introduced new handles, we can reset their file index - which is the index into the ioinfo array.
107         // This hooks up the standard cruntime APIS to the new console, allowing a functional output.  As for input YMMV.
108         stdout->_file = 0;
109         stderr->_file = 0;
110     }
111 }
112
113 ////////////////// Setup Windows Specific Interface //////////////////
114
115 void registerDroseraClass(HINSTANCE hInstance)
116 {
117     WNDCLASSEX wcex;
118
119     wcex.cbSize = sizeof(WNDCLASSEX);
120
121     wcex.style         = CS_HREDRAW | CS_VREDRAW;
122     wcex.lpfnWndProc   = ::droseraWndProc;
123     wcex.cbClsExtra    = 0;
124     wcex.cbWndExtra    = sizeof(Drosera*);
125     wcex.hInstance     = hInstance;
126     wcex.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DROSERA));
127     wcex.hCursor       = LoadCursor(0, IDC_ARROW);
128     wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
129     wcex.lpszMenuName  = MAKEINTRESOURCE(IDC_DROSERA);
130     wcex.lpszClassName = szWindowClass;
131     wcex.hIconSm       = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
132
133     RegisterClassEx(&wcex);
134 }
135
136 //Processes messages for the main window.
137 LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
138 {
139     PAINTSTRUCT ps;
140     HDC hdc;
141
142     LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0);
143     Drosera* drosera = reinterpret_cast<Drosera*>(longPtr);
144
145     switch (message) {
146         case WM_COMMAND:
147             return drosera->handleCommand(hWnd, message, wParam, lParam);
148             break;
149         case WM_SIZE:
150             if (!drosera)
151                 return 0;
152             return drosera->webViewLoaded() ? drosera->onSize(wParam, lParam) : 0;
153         case WM_PAINT:
154             hdc = BeginPaint(hWnd, &ps);
155             EndPaint(hWnd, &ps);
156             break;
157         case WM_DESTROY:
158             PostQuitMessage(0);
159             break;
160         default:
161             return DefWindowProc(hWnd, message, wParam, lParam);
162     }
163
164     return 0;
165 }
166
167 LRESULT CALLBACK Drosera::handleCommand(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
168 {
169     int wmId = LOWORD(wParam);
170     switch (wmId) {
171         case ID_DEBUG_CONTINUE:
172             m_debuggerClient->resume();
173             break;
174         case ID_DEBUG_PAUSE:
175             m_debuggerClient->pause();
176             break;
177         case ID_DEBUG_STEPINTO:
178             m_debuggerClient->stepInto();
179             break;
180         case ID_DEBUG_STEPOVER:
181             m_debuggerClient->stepOver();
182             break;
183         case ID_DEBUG_STEPOUT:
184             m_debuggerClient->stepOut();
185             break;
186         case ID_DEBUG_SHOWCONSOLE:
187             m_debuggerClient->showConsole();
188             break;
189         case ID_HELP_ABOUT:
190             DialogBox(Drosera::getInst(), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, ::aboutWndProc);
191             break;
192         case ID_FILE_EXIT:
193             DestroyWindow(hWnd);
194             break;
195         default:
196             return DefWindowProc(hWnd, message, wParam, lParam);
197     }
198
199     return 0;
200 }
201
202 // Message handler for about box.
203 INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
204 {
205     UNREFERENCED_PARAMETER(lParam);
206     switch (message) {
207         case WM_INITDIALOG:
208             return (INT_PTR)TRUE;
209
210         case WM_COMMAND:
211             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
212                 EndDialog(hDlg, LOWORD(wParam));
213                 return (INT_PTR)TRUE;
214             }
215             break;
216     }
217     return (INT_PTR)FALSE;
218 }
219
220 ////////////////// End Setup Windows Specific Interface //////////////////
221
222 Drosera::Drosera()
223     : m_hWnd(0)
224     , m_debuggerClient(new DebuggerClient())
225 {
226 }
227
228 HRESULT Drosera::init(HINSTANCE hInstance, int nCmdShow)
229 {
230     HRESULT ret = initUI(hInstance, nCmdShow);
231     if (FAILED(ret))
232         return ret;
233
234     ret = attach();
235     return ret;
236 }
237
238
239 HRESULT Drosera::initUI(HINSTANCE hInstance, int nCmdShow)
240 {
241     // Initialize global strings
242     LoadString(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
243     LoadString(hInstance, IDC_DROSERA, szWindowClass, ARRAYSIZE(szWindowClass));
244     registerDroseraClass(hInstance);
245
246     Drosera::setInst(hInstance); // Store instance handle in our local variable
247
248     m_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
249         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0);
250
251     if (!m_hWnd)
252         return HRESULT_FROM_WIN32(GetLastError());
253
254     SetLastError(0);
255     SetWindowLongPtr(m_hWnd, 0, reinterpret_cast<LONG_PTR>(this));
256     HRESULT ret = HRESULT_FROM_WIN32(GetLastError());
257     if (FAILED(ret))
258         return ret;
259
260     CLSID clsid = CLSID_NULL;
261     ret = CLSIDFromProgID(PROGID(WebView), &clsid);
262     if (FAILED(ret))
263         return ret;
264
265     ret = CoCreateInstance(clsid, 0, CLSCTX_ALL, IID_IWebView, (void**)&m_webView);
266     if (FAILED(ret))
267         return ret;
268
269     m_webViewPrivate.query(m_webView.get());
270     if (!m_webViewPrivate)
271         return E_FAIL;
272
273     ret = m_webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_hWnd));
274     if (FAILED(ret))
275         return ret;
276
277     RECT clientRect = {0};
278     ::GetClientRect(m_hWnd, &clientRect);
279     ret = m_webView->initWithFrame(clientRect, 0, 0);
280     if (FAILED(ret))
281         return ret;
282
283     HWND viewWindow;
284     ret = m_webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow));
285     if (FAILED(ret))
286         return ret;
287
288     SetProp(viewWindow, s_DroseraPointerProp, (HANDLE)this);
289
290     // FIXME: Implement window size/position save/restore
291     ShowWindow(m_hWnd, nCmdShow);
292     UpdateWindow(m_hWnd);
293
294     return ret;
295 }
296
297 LRESULT Drosera::onSize(WPARAM, LPARAM)
298 {
299     if (!m_webViewPrivate)
300         return 0;
301
302     RECT clientRect = {0};
303     ::GetClientRect(m_hWnd, &clientRect);
304
305     HWND viewWindow;
306     if (SUCCEEDED(m_webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
307         // FIXME should this be the height-command bars height?
308         ::SetWindowPos(viewWindow, 0, clientRect.left, clientRect.top, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_NOZORDER);
309
310     return 0;
311 }
312
313 bool Drosera::webViewLoaded() const
314 {
315     return m_debuggerClient->webViewLoaded();
316 }
317
318 // Server Detection Callbacks
319
320 HRESULT Drosera::attach()
321 {
322     // Get selected server
323     HRESULT ret = m_webView->setFrameLoadDelegate(m_debuggerClient.get());
324     if (FAILED(ret))
325         return ret;
326
327     ret = m_webView->setUIDelegate(m_debuggerClient.get());
328     if (FAILED(ret))
329         return ret;
330
331     CLSID clsid = CLSID_NULL;
332     ret = CLSIDFromProgID(PROGID(WebMutableURLRequest), &clsid);
333     if (FAILED(ret))
334         return ret;
335
336     COMPtr<IWebMutableURLRequest> request;
337     ret = CoCreateInstance(clsid, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request);
338     if (FAILED(ret))
339         return ret;
340
341     RetainPtr<CFURLRef> htmlURLRef(AdoptCF, ::CFBundleCopyResourceURL(::CFBundleGetBundleWithIdentifier(CFSTR("org.webkit.drosera")), CFSTR("debugger"), CFSTR("html"), CFSTR("Drosera")));
342     if (!htmlURLRef)
343         return E_FAIL;
344
345     CFStringRef urlStringRef = ::CFURLGetString(htmlURLRef.get());
346     BSTR tempStr = cfStringToBSTR(urlStringRef);    // Both initWithRUL and SysFreeString can handle 0.
347     ret = request->initWithURL(tempStr, WebURLRequestUseProtocolCachePolicy, 60);
348     SysFreeString(tempStr);
349     if (FAILED(ret))
350         return ret;
351
352     COMPtr<IWebFrame> mainFrame;
353     ret = m_webView->mainFrame(mainFrame.adoptionPointer());
354     if (FAILED(ret))
355         return ret;
356
357     ret = mainFrame->loadRequest(request.get());
358     if (FAILED(ret))
359         return ret;
360
361     return ret;
362 }
363
364 BSTR cfStringToBSTR(CFStringRef cfstr)
365 {
366     if (!cfstr)
367         return 0;
368
369     const UniChar* uniChars = CFStringGetCharactersPtr(cfstr);
370     if (uniChars)
371         return SysAllocStringLen((LPCTSTR)uniChars, CFStringGetLength(cfstr));
372
373     CFIndex length = CFStringGetLength(cfstr);
374     BSTR bstr = SysAllocStringLen(0, length);
375     CFStringGetCharacters(cfstr, CFRangeMake(0, length), (UniChar*)bstr);
376     bstr[length] = 0;
377
378     return bstr;
379 }
380
381 // Server Connection Functions
382
383 bool Drosera::serverConnected() const
384 {
385     return m_debuggerClient->serverConnected();
386 }
387
388 void Drosera::attemptToCreateServerConnection()
389 {
390     m_debuggerClient->attemptToCreateServerConnection();
391 }
392