|
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 "HelperFunctions.h" |
|
35 #include "resource.h" |
|
36 |
|
37 #include <JavaScriptCore/JSStringRef.h> |
|
38 #include <WebCore/IntRect.h> |
|
39 #include <WebKit/IWebMutableURLRequest.h> |
|
40 #include <WebKit/IWebView.h> |
|
41 #include <WebKit/WebKit.h> |
|
42 #include <windowsx.h> |
|
43 |
|
44 const unsigned MAX_LOADSTRING = 100; |
|
45 |
|
46 TCHAR szTitle[MAX_LOADSTRING]; // The title bar text |
|
47 TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name |
|
48 |
|
49 static const LRESULT kNotHandledResult = -1; |
|
50 static LPCTSTR kDroseraPointerProp = TEXT("DroseraPointer"); |
|
51 HINSTANCE Drosera::m_hInst(0); |
|
52 |
|
53 extern "C" BOOL InitializeCoreGraphics(); |
|
54 |
|
55 ATOM registerDroseraClass(HINSTANCE hInstance); |
|
56 LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); |
|
57 INT_PTR CALLBACK attachWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); |
|
58 INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); |
|
59 |
|
60 int APIENTRY _tWinMain(HINSTANCE hInstance, |
|
61 HINSTANCE hPrevInstance, |
|
62 LPTSTR lpCmdLine, |
|
63 int nCmdShow) |
|
64 { |
|
65 UNREFERENCED_PARAMETER(hPrevInstance); |
|
66 UNREFERENCED_PARAMETER(lpCmdLine); |
|
67 |
|
68 MSG msg; |
|
69 |
|
70 InitializeCoreGraphics(); |
|
71 |
|
72 Drosera drosera; |
|
73 |
|
74 HRESULT ret = drosera.initUI(hInstance, nCmdShow); |
|
75 if (FAILED(ret)) |
|
76 return ret; |
|
77 |
|
78 HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DROSERA)); |
|
79 |
|
80 // Main message loop: |
|
81 while (GetMessage(&msg, 0, 0, 0)) { |
|
82 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { |
|
83 TranslateMessage(&msg); |
|
84 DispatchMessage(&msg); |
|
85 } |
|
86 } |
|
87 |
|
88 return static_cast<int>(msg.wParam); |
|
89 } |
|
90 |
|
91 ////////////////// Setup Windows Specific Interface ////////////////// |
|
92 |
|
93 ATOM registerDroseraClass(HINSTANCE hInstance) |
|
94 { |
|
95 WNDCLASSEX wcex; |
|
96 |
|
97 wcex.cbSize = sizeof(WNDCLASSEX); |
|
98 |
|
99 wcex.style = CS_HREDRAW | CS_VREDRAW; |
|
100 wcex.lpfnWndProc = ::droseraWndProc; |
|
101 wcex.cbClsExtra = 0; |
|
102 wcex.cbWndExtra = sizeof(Drosera*); |
|
103 wcex.hInstance = hInstance; |
|
104 wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DROSERA)); |
|
105 wcex.hCursor = LoadCursor(0, IDC_ARROW); |
|
106 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); |
|
107 wcex.lpszMenuName = MAKEINTRESOURCE(IDC_DROSERA); |
|
108 wcex.lpszClassName = szWindowClass; |
|
109 wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); |
|
110 |
|
111 return RegisterClassEx(&wcex); |
|
112 } |
|
113 |
|
114 //Processes messages for the main window. |
|
115 LRESULT CALLBACK droseraWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
|
116 { |
|
117 int wmId, wmEvent; |
|
118 PAINTSTRUCT ps; |
|
119 HDC hdc; |
|
120 |
|
121 LONG_PTR longPtr = GetWindowLongPtr(hWnd, 0); |
|
122 Drosera* drosera = reinterpret_cast<Drosera*>(longPtr); |
|
123 |
|
124 switch (message) { |
|
125 case WM_COMMAND: |
|
126 wmId = LOWORD(wParam); |
|
127 wmEvent = HIWORD(wParam); |
|
128 switch (wmId) { |
|
129 case ID_HELP_ABOUT: |
|
130 DialogBox(Drosera::getInst(), MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, ::aboutWndProc); |
|
131 break; |
|
132 case ID_FILE_ATTACH: |
|
133 DialogBox(Drosera::getInst(), MAKEINTRESOURCE(IDD_ATTACH), hWnd, ::attachWndProc); |
|
134 break; |
|
135 case ID_FILE_EXIT: |
|
136 DestroyWindow(hWnd); |
|
137 break; |
|
138 default: |
|
139 return DefWindowProc(hWnd, message, wParam, lParam); |
|
140 } |
|
141 break; |
|
142 case WM_SIZE: |
|
143 if (!drosera) |
|
144 return 0; |
|
145 return drosera->webViewLoaded() ? drosera->onSize(wParam, lParam) : 0; |
|
146 case WM_PAINT: |
|
147 hdc = BeginPaint(hWnd, &ps); |
|
148 // TODO: Add any drawing code here... |
|
149 EndPaint(hWnd, &ps); |
|
150 break; |
|
151 case WM_DESTROY: |
|
152 PostQuitMessage(0); |
|
153 break; |
|
154 default: |
|
155 return DefWindowProc(hWnd, message, wParam, lParam); |
|
156 } |
|
157 return 0; |
|
158 } |
|
159 |
|
160 // Message handler for about box. |
|
161 INT_PTR CALLBACK aboutWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) |
|
162 { |
|
163 UNREFERENCED_PARAMETER(lParam); |
|
164 switch (message) { |
|
165 case WM_INITDIALOG: |
|
166 return (INT_PTR)TRUE; |
|
167 |
|
168 case WM_COMMAND: |
|
169 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { |
|
170 EndDialog(hDlg, LOWORD(wParam)); |
|
171 return (INT_PTR)TRUE; |
|
172 } |
|
173 break; |
|
174 } |
|
175 return (INT_PTR)FALSE; |
|
176 } |
|
177 |
|
178 // Message handler for Attach box. |
|
179 INT_PTR CALLBACK attachWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) |
|
180 { |
|
181 UNREFERENCED_PARAMETER(lParam); |
|
182 switch (message) { |
|
183 case WM_INITDIALOG: |
|
184 return (INT_PTR)TRUE; |
|
185 |
|
186 case WM_COMMAND: |
|
187 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { |
|
188 EndDialog(hDlg, LOWORD(wParam)); |
|
189 return (INT_PTR)TRUE; |
|
190 } |
|
191 break; |
|
192 } |
|
193 return (INT_PTR)FALSE; |
|
194 } |
|
195 |
|
196 ////////////////// End Setup Windows Specific Interface ////////////////// |
|
197 |
|
198 Drosera::Drosera() |
|
199 : m_hWnd(0) |
|
200 , m_webViewLoaded(false) |
|
201 , m_debuggerDocument(new DebuggerClient()) |
|
202 { |
|
203 } |
|
204 |
|
205 HRESULT Drosera::initUI(HINSTANCE hInstance, int nCmdShow) |
|
206 { |
|
207 // Initialize global strings |
|
208 LoadString(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle)); |
|
209 LoadString(hInstance, IDC_DROSERA, szWindowClass, ARRAYSIZE(szWindowClass)); |
|
210 registerDroseraClass(hInstance); |
|
211 |
|
212 Drosera::setInst(hInstance); // Store instance handle in our local variable |
|
213 |
|
214 m_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, |
|
215 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0); |
|
216 |
|
217 if (!m_hWnd) |
|
218 return HRESULT_FROM_WIN32(GetLastError()); |
|
219 |
|
220 SetLastError(0); |
|
221 SetWindowLongPtr(m_hWnd, 0, reinterpret_cast<LONG_PTR>(this)); |
|
222 HRESULT ret = HRESULT_FROM_WIN32(GetLastError()); |
|
223 if (FAILED(ret)) |
|
224 return ret; |
|
225 |
|
226 ret = OleInitialize(0); |
|
227 if (FAILED(ret)) |
|
228 return ret; |
|
229 |
|
230 ret = CoCreateInstance(CLSID_WebView, 0, CLSCTX_ALL, IID_IWebView, (void**)&m_webView); |
|
231 if (FAILED(ret)) |
|
232 return ret; |
|
233 |
|
234 ret = m_webView->QueryInterface(IID_IWebViewPrivate, reinterpret_cast<void**>(&m_webViewPrivate)); |
|
235 if (FAILED(ret)) |
|
236 return ret; |
|
237 |
|
238 ret = m_webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_hWnd)); |
|
239 if (FAILED(ret)) |
|
240 return ret; |
|
241 |
|
242 ret = m_webView->setFrameLoadDelegate(this); |
|
243 if (FAILED(ret)) |
|
244 return ret; |
|
245 |
|
246 ret = m_webView->setUIDelegate(this); |
|
247 if (FAILED(ret)) |
|
248 return ret; |
|
249 |
|
250 RECT rect = {0}; |
|
251 ret = m_webView->initWithFrame(rect, 0, 0); |
|
252 if (FAILED(ret)) |
|
253 return ret; |
|
254 |
|
255 HWND viewWindow; |
|
256 ret = m_webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow)); |
|
257 if (FAILED(ret)) |
|
258 return ret; |
|
259 |
|
260 ::SetProp(viewWindow, kDroseraPointerProp, (HANDLE)this); |
|
261 |
|
262 COMPtr<IWebFrame> mainFrame; |
|
263 ret = m_webView->mainFrame(&mainFrame); |
|
264 if (FAILED(ret)) |
|
265 return ret; |
|
266 |
|
267 COMPtr<IWebMutableURLRequest> request; |
|
268 ret = CoCreateInstance(CLSID_WebMutableURLRequest, 0, CLSCTX_ALL, IID_IWebMutableURLRequest, (void**)&request); |
|
269 if (FAILED(ret)) |
|
270 return ret; |
|
271 |
|
272 RetainPtr<CFURLRef> htmlURLRef(AdoptCF, ::CFBundleCopyResourceURL(::CFBundleGetBundleWithIdentifier(CFSTR("org.webkit.drosera")), CFSTR("debugger"), CFSTR("html"), CFSTR("Drosera"))); |
|
273 if (!htmlURLRef) |
|
274 return E_FAIL; |
|
275 |
|
276 CFStringRef urlStringRef = ::CFURLGetString(htmlURLRef.get()); |
|
277 BSTR tempStr = cfStringToBSTR(urlStringRef); // Both initWithRUL and SysFreeString can handle 0. |
|
278 ret = request->initWithURL(tempStr, WebURLRequestUseProtocolCachePolicy, 60); |
|
279 SysFreeString(tempStr); |
|
280 if (FAILED(ret)) |
|
281 return ret; |
|
282 |
|
283 ret = mainFrame->loadRequest(request.get()); |
|
284 if (FAILED(ret)) |
|
285 return ret; |
|
286 |
|
287 m_webViewLoaded = true; |
|
288 |
|
289 // FIXME: Implement window size/position save/restore |
|
290 |
|
291 RECT frame; |
|
292 frame.left = 60; |
|
293 frame.top = 200; |
|
294 frame.right = 750; |
|
295 frame.bottom = 550; |
|
296 ::SetWindowPos(m_hWnd, HWND_TOPMOST, frame.left, frame.top, frame.right - frame.left, frame.bottom - frame.top, 0); |
|
297 ShowWindow(m_hWnd, nCmdShow); |
|
298 UpdateWindow(m_hWnd); |
|
299 |
|
300 return 0; |
|
301 } |
|
302 |
|
303 // IUnknown ------------------------------ |
|
304 HRESULT STDMETHODCALLTYPE Drosera::QueryInterface(REFIID riid, void** ppvObject) // how to cast between implemented interfaces |
|
305 { |
|
306 *ppvObject = 0; |
|
307 if (IsEqualGUID(riid, IID_IUnknown)) |
|
308 *ppvObject = this; |
|
309 else if (IsEqualGUID(riid, IID_IWebFrameLoadDelegate)) |
|
310 *ppvObject = static_cast<IWebFrameLoadDelegate*>(this); |
|
311 else if (IsEqualGUID(riid, IID_IWebUIDelegate)) |
|
312 *ppvObject = static_cast<IWebUIDelegate*>(this); |
|
313 else |
|
314 return E_NOINTERFACE; |
|
315 |
|
316 AddRef(); |
|
317 return S_OK; |
|
318 } |
|
319 |
|
320 ULONG STDMETHODCALLTYPE Drosera::AddRef(void) |
|
321 { // COM ref-counting isn't useful to us because we're in charge of the lifetime of the WebView. |
|
322 // We use the number 2 because of some idiosycracy with COM that expects us to be referenced twice. |
|
323 return 2; |
|
324 } |
|
325 |
|
326 ULONG STDMETHODCALLTYPE Drosera::Release(void) |
|
327 { // COM ref-counting isn't useful to us because we're in charge of the lifetime of the WebView. |
|
328 // We use the number 2 because of some idiosycracy with COM that expects us to be referenced twice. |
|
329 return 2; |
|
330 } |
|
331 |
|
332 // IWebFrameLoadDelegate ------------------------------ |
|
333 HRESULT STDMETHODCALLTYPE Drosera::didFinishLoadForFrame( |
|
334 /* [in] */ IWebView* m_webView, |
|
335 /* [in] */ IWebFrame* frame) |
|
336 { |
|
337 // note: this is Drosera's own WebView, not the one in the app that we are attached to. |
|
338 m_webViewLoaded = true; |
|
339 |
|
340 COMPtr<IWebFrame> mainFrame; |
|
341 HRESULT hr = m_webView->mainFrame(&mainFrame); |
|
342 if (FAILED(hr)) |
|
343 return hr; |
|
344 |
|
345 if (mainFrame != frame) // FIXME Replace below with X Drosera |
|
346 return S_OK; |
|
347 |
|
348 COMPtr<IDOMDocument> document; |
|
349 hr = mainFrame->DOMDocument(&document); |
|
350 if (FAILED(hr)) |
|
351 return hr; |
|
352 |
|
353 COMPtr<IDOMHTMLDocument> htmlDocument; |
|
354 hr = document->QueryInterface(IID_IDOMHTMLDocument, reinterpret_cast<void**>(&htmlDocument)); |
|
355 if (FAILED(hr)) |
|
356 return hr; |
|
357 |
|
358 return S_OK; |
|
359 } |
|
360 |
|
361 HRESULT STDMETHODCALLTYPE Drosera::windowScriptObjectAvailable( |
|
362 /* [in] */ IWebView*, |
|
363 /* [in] */ JSContextRef context, |
|
364 /* [in] */ JSObjectRef windowObject) |
|
365 { |
|
366 |
|
367 JSValueRef exception = 0; |
|
368 m_debuggerDocument.windowScriptObjectAvailable(context, windowObject, &exception); |
|
369 if (exception) |
|
370 return S_FALSE; |
|
371 |
|
372 return S_OK; |
|
373 } |
|
374 |
|
375 HRESULT STDMETHODCALLTYPE Drosera::runJavaScriptAlertPanelWithMessage( // For debugging purposes |
|
376 /* [in] */ IWebView*, |
|
377 /* [in] */ BSTR message) |
|
378 { |
|
379 #ifndef NDEBUG |
|
380 fwprintf(stderr, L"%s\n", message ? message : L""); |
|
381 #else |
|
382 (void)message; |
|
383 #endif |
|
384 return S_OK; |
|
385 } |
|
386 |
|
387 LRESULT Drosera::onSize(WPARAM, LPARAM) |
|
388 { |
|
389 if (!m_webViewPrivate) |
|
390 return 0; |
|
391 |
|
392 RECT clientRect = {0}; |
|
393 ::GetClientRect(m_hWnd, &clientRect); |
|
394 |
|
395 HWND viewWindow; |
|
396 if (FAILED(m_webViewPrivate->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow)))) |
|
397 return 0; |
|
398 |
|
399 // FIXME should this be the height-command bars height? |
|
400 ::SetWindowPos(viewWindow, 0, clientRect.left, clientRect.top, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, SWP_NOZORDER); |
|
401 return 0; |
|
402 } |
|
403 |
|
404 void Drosera::initWithServerName(std::wstring* /*serverName*/) |
|
405 { |
|
406 // m_debuggerDocument = new DebuggerDocument(m_debuggerClient); |
|
407 |
|
408 //if ((self = [super init])) |
|
409 // [self switchToServerNamed:serverName]; |
|
410 //return self; |
|
411 } |
|
412 |
|
413 void Drosera::switchToServerNamed(std::wstring* /*server*/) |
|
414 { |
|
415 //if (server) { |
|
416 // [[NSNotificationCenter defaultCenter] removeObserver:self name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]]; |
|
417 // if ([[(NSDistantObject *)server connectionForProxy] isValid]) { |
|
418 // [server removeListener:self]; |
|
419 // [self resume]; |
|
420 // } |
|
421 //} |
|
422 |
|
423 //id old = server; |
|
424 //server = ([name length] ? [[NSConnection rootProxyForConnectionWithRegisteredName:name host:nil] retain] : nil); |
|
425 //[old release]; |
|
426 |
|
427 //old = currentServerName; |
|
428 //currentServerName = [name copy]; |
|
429 //[old release]; |
|
430 |
|
431 //if (server) { |
|
432 // @try { |
|
433 // [(NSDistantObject *)server setProtocolForProxy:@protocol(WebScriptDebugServer)]; |
|
434 // [server addListener:self]; |
|
435 // } @catch (NSException *exception) { |
|
436 // [currentServerName release]; |
|
437 // currentServerName = nil; |
|
438 // [server release]; |
|
439 // server = nil; |
|
440 // } |
|
441 |
|
442 // if (server) |
|
443 // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(serverConnectionDidDie:) name:NSConnectionDidDieNotification object:[(NSDistantObject *)server connectionForProxy]]; |
|
444 //} |
|
445 } |