1 /*
2 * Copyright (C) 2006, 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 "WebInspectorClient.h"
31
32 #include "WebInspectorDelegate.h"
33 #include "WebKit.h"
34 #include "WebMutableURLRequest.h"
35 #include "WebNodeHighlight.h"
36 #include "WebView.h"
37
38 #pragma warning(push, 0)
39 #include <WebCore/BString.h>
40 #include <WebCore/Element.h>
41 #include <WebCore/FloatRect.h>
42 #include <WebCore/FrameView.h>
43 #include <WebCore/InspectorController.h>
44 #include <WebCore/Page.h>
45 #include <WebCore/RenderObject.h>
46 #include <WebCore/WindowMessageBroadcaster.h>
47 #pragma warning(pop)
48
49 #include <tchar.h>
50 #include <wtf/RetainPtr.h>
51
52 using namespace WebCore;
53
54 static LPCTSTR kWebInspectorWindowClassName = TEXT("WebInspectorWindowClass");
55 static ATOM registerWindowClass();
56 static LPCTSTR kWebInspectorPointerProp = TEXT("WebInspectorPointer");
57
58 static const unsigned defaultAttachedHeight = 300;
59
defaultWindowRect()60 static const IntRect& defaultWindowRect()
61 {
62 static IntRect rect(60, 200, 750, 650);
63 return rect;
64 }
65
getWebKitBundle()66 static CFBundleRef getWebKitBundle()
67 {
68 return CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
69 }
70
WebInspectorClient(WebView * webView)71 WebInspectorClient::WebInspectorClient(WebView* webView)
72 : m_inspectedWebView(webView)
73 , m_hwnd(0)
74 , m_webViewHwnd(0)
75 , m_shouldAttachWhenShown(false)
76 , m_attached(false)
77 {
78 ASSERT(m_inspectedWebView);
79
80 m_inspectedWebView->viewWindow((OLE_HANDLE*)&m_inspectedWebViewHwnd);
81
82 // FIXME: Implement window size/position save/restore
83 #if 0
84 [self setWindowFrameAutosaveName:@"Web Inspector"];
85 #endif
86 }
87
~WebInspectorClient()88 WebInspectorClient::~WebInspectorClient()
89 {
90 if (m_hwnd)
91 ::DestroyWindow(m_hwnd);
92 }
93
inspectorDestroyed()94 void WebInspectorClient::inspectorDestroyed()
95 {
96 delete this;
97 }
98
createPage()99 Page* WebInspectorClient::createPage()
100 {
101 if (m_webView)
102 return core(m_webView.get());
103
104 ASSERT(!m_hwnd);
105
106 registerWindowClass();
107
108 m_hwnd = ::CreateWindowEx(0, kWebInspectorWindowClassName, 0, WS_OVERLAPPEDWINDOW,
109 defaultWindowRect().x(), defaultWindowRect().y(), defaultWindowRect().width(), defaultWindowRect().height(),
110 0, 0, 0, 0);
111
112 if (!m_hwnd)
113 return 0;
114
115 ::SetProp(m_hwnd, kWebInspectorPointerProp, reinterpret_cast<HANDLE>(this));
116
117 m_webView.adoptRef(WebView::createInstance());
118
119 if (FAILED(m_webView->setHostWindow((OLE_HANDLE)(ULONG64)m_hwnd)))
120 return 0;
121
122 RECT rect;
123 GetClientRect(m_hwnd, &rect);
124 if (FAILED(m_webView->initWithFrame(rect, 0, 0)))
125 return 0;
126
127 COMPtr<WebInspectorDelegate> delegate(AdoptCOM, WebInspectorDelegate::createInstance());
128 if (FAILED(m_webView->setUIDelegate(delegate.get())))
129 return 0;
130
131 // Keep preferences separate from the rest of the client, making sure we are using expected preference values.
132 // One reason this is good is that it keeps the inspector out of history via "private browsing".
133 // FIXME: It's crazy that we have to do this song and dance to end up with
134 // a private WebPreferences object, even within WebKit. We should make this
135 // process simpler, and consider whether we can make it simpler for WebKit
136 // clients as well.
137 COMPtr<WebPreferences> tempPreferences(AdoptCOM, WebPreferences::createInstance());
138 COMPtr<IWebPreferences> iPreferences;
139 if (FAILED(tempPreferences->initWithIdentifier(BString(L"WebInspectorPreferences"), &iPreferences)))
140 return 0;
141 COMPtr<WebPreferences> preferences(Query, iPreferences);
142 if (!preferences)
143 return 0;
144 if (FAILED(preferences->setAutosaves(FALSE)))
145 return 0;
146 if (FAILED(preferences->setPrivateBrowsingEnabled(TRUE)))
147 return 0;
148 if (FAILED(preferences->setLoadsImagesAutomatically(TRUE)))
149 return 0;
150 if (FAILED(preferences->setAuthorAndUserStylesEnabled(TRUE)))
151 return 0;
152 if (FAILED(preferences->setAllowsAnimatedImages(TRUE)))
153 return 0;
154 if (FAILED(preferences->setLoadsImagesAutomatically(TRUE)))
155 return 0;
156 if (FAILED(preferences->setPlugInsEnabled(FALSE)))
157 return 0;
158 if (FAILED(preferences->setJavaEnabled(FALSE)))
159 return 0;
160 if (FAILED(preferences->setUserStyleSheetEnabled(FALSE)))
161 return 0;
162 if (FAILED(preferences->setTabsToLinks(FALSE)))
163 return 0;
164 if (FAILED(preferences->setMinimumFontSize(0)))
165 return 0;
166 if (FAILED(preferences->setMinimumLogicalFontSize(9)))
167 return 0;
168
169 if (FAILED(m_webView->setPreferences(preferences.get())))
170 return 0;
171
172 m_webView->setProhibitsMainFrameScrolling(TRUE);
173
174 if (FAILED(m_webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&m_webViewHwnd))))
175 return 0;
176
177 COMPtr<WebMutableURLRequest> request;
178 request.adoptRef(WebMutableURLRequest::createInstance());
179
180 RetainPtr<CFURLRef> htmlURLRef(AdoptCF, CFBundleCopyResourceURL(getWebKitBundle(), CFSTR("inspector"), CFSTR("html"), CFSTR("inspector")));
181 if (!htmlURLRef)
182 return 0;
183
184 CFStringRef urlStringRef = ::CFURLGetString(htmlURLRef.get());
185 if (FAILED(request->initWithURL(BString(urlStringRef), WebURLRequestUseProtocolCachePolicy, 60)))
186 return 0;
187
188 if (FAILED(m_webView->topLevelFrame()->loadRequest(request.get())))
189 return 0;
190
191 return core(m_webView.get());
192 }
193
194
localizedStringsURL()195 String WebInspectorClient::localizedStringsURL()
196 {
197 RetainPtr<CFURLRef> url(AdoptCF, CFBundleCopyResourceURL(getWebKitBundle(), CFSTR("localizedStrings"), CFSTR("js"), 0));
198 if (!url)
199 return String();
200
201 return CFURLGetString(url.get());
202 }
203
showWindow()204 void WebInspectorClient::showWindow()
205 {
206 showWindowWithoutNotifications();
207 m_inspectedWebView->page()->inspectorController()->setWindowVisible(true);
208 }
209
closeWindow()210 void WebInspectorClient::closeWindow()
211 {
212 closeWindowWithoutNotifications();
213 m_inspectedWebView->page()->inspectorController()->setWindowVisible(false);
214 }
215
windowVisible()216 bool WebInspectorClient::windowVisible()
217 {
218 return !!::IsWindowVisible(m_hwnd);
219 }
220
attachWindow()221 void WebInspectorClient::attachWindow()
222 {
223 if (m_attached)
224 return;
225
226 m_shouldAttachWhenShown = true;
227
228 closeWindowWithoutNotifications();
229 showWindowWithoutNotifications();
230 }
231
detachWindow()232 void WebInspectorClient::detachWindow()
233 {
234 if (!m_attached)
235 return;
236
237 m_shouldAttachWhenShown = false;
238
239 closeWindowWithoutNotifications();
240 showWindowWithoutNotifications();
241 }
242
setAttachedWindowHeight(unsigned height)243 void WebInspectorClient::setAttachedWindowHeight(unsigned height)
244 {
245 // FIXME: implement this.
246 }
247
highlight(Node *)248 void WebInspectorClient::highlight(Node*)
249 {
250 bool creatingHighlight = !m_highlight;
251
252 if (creatingHighlight)
253 m_highlight.set(new WebNodeHighlight(m_inspectedWebView));
254
255 if (m_highlight->isShowing())
256 m_highlight->update();
257 else
258 m_highlight->setShowsWhileWebViewIsVisible(true);
259
260 if (creatingHighlight && IsWindowVisible(m_hwnd))
261 m_highlight->placeBehindWindow(m_hwnd);
262 }
263
hideHighlight()264 void WebInspectorClient::hideHighlight()
265 {
266 if (m_highlight)
267 m_highlight->setShowsWhileWebViewIsVisible(false);
268 }
269
inspectedURLChanged(const String & newURL)270 void WebInspectorClient::inspectedURLChanged(const String& newURL)
271 {
272 m_inspectedURL = newURL;
273 updateWindowTitle();
274 }
275
closeWindowWithoutNotifications()276 void WebInspectorClient::closeWindowWithoutNotifications()
277 {
278 if (!m_hwnd)
279 return;
280
281 if (!m_attached) {
282 ShowWindow(m_hwnd, SW_HIDE);
283 return;
284 }
285
286 ASSERT(m_webView);
287 ASSERT(m_inspectedWebViewHwnd);
288 ASSERT(!IsWindowVisible(m_hwnd));
289
290 // Remove the Inspector's WebView from the inspected WebView's parent window.
291 WindowMessageBroadcaster::removeListener(m_inspectedWebViewHwnd, this);
292
293 m_attached = false;
294
295 m_webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_hwnd));
296
297 // Make sure everything has the right size/position.
298 HWND hostWindow;
299 if (SUCCEEDED(m_inspectedWebView->hostWindow((OLE_HANDLE*)&hostWindow)))
300 SendMessage(hostWindow, WM_SIZE, 0, 0);
301
302 if (m_highlight && m_highlight->isShowing())
303 m_highlight->update();
304 }
305
showWindowWithoutNotifications()306 void WebInspectorClient::showWindowWithoutNotifications()
307 {
308 if (!m_hwnd)
309 return;
310
311 ASSERT(m_webView);
312 ASSERT(m_inspectedWebViewHwnd);
313
314 if (!m_shouldAttachWhenShown) {
315 // Put the Inspector's WebView inside our window and show it.
316 m_webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_hwnd));
317 SendMessage(m_hwnd, WM_SIZE, 0, 0);
318 updateWindowTitle();
319
320 SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
321 return;
322 }
323
324 // Put the Inspector's WebView inside the inspected WebView's parent window.
325 WindowMessageBroadcaster::addListener(m_inspectedWebViewHwnd, this);
326
327 HWND hostWindow;
328 if (FAILED(m_inspectedWebView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow))))
329 return;
330
331 m_webView->setHostWindow(reinterpret_cast<OLE_HANDLE>(hostWindow));
332
333 // Then hide our own window.
334 ShowWindow(m_hwnd, SW_HIDE);
335
336 m_attached = true;
337
338 // Make sure everything has the right size/position.
339 SendMessage(hostWindow, WM_SIZE, 0, 0);
340 if (m_highlight && m_highlight->isShowing())
341 m_highlight->update();
342 }
343
updateWindowTitle()344 void WebInspectorClient::updateWindowTitle()
345 {
346 // FIXME: The series of appends should be replaced with a call to String::format()
347 // when it can be figured out how to get the unicode em-dash to show up.
348 String title = "Web Inspector ";
349 title.append((UChar)0x2014); // em-dash
350 title.append(' ');
351 title.append(m_inspectedURL);
352 ::SetWindowText(m_hwnd, title.charactersWithNullTermination());
353 }
354
onGetMinMaxInfo(WPARAM,LPARAM lParam)355 LRESULT WebInspectorClient::onGetMinMaxInfo(WPARAM, LPARAM lParam)
356 {
357 MINMAXINFO* info = reinterpret_cast<MINMAXINFO*>(lParam);
358 POINT size = {400, 400};
359 info->ptMinTrackSize = size;
360
361 return 0;
362 }
363
onSize(WPARAM,LPARAM)364 LRESULT WebInspectorClient::onSize(WPARAM, LPARAM)
365 {
366 RECT rect;
367 ::GetClientRect(m_hwnd, &rect);
368
369 ::SetWindowPos(m_webViewHwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
370
371 return 0;
372 }
373
onClose(WPARAM,LPARAM)374 LRESULT WebInspectorClient::onClose(WPARAM, LPARAM)
375 {
376 ::ShowWindow(m_hwnd, SW_HIDE);
377 m_inspectedWebView->page()->inspectorController()->setWindowVisible(false);
378
379 hideHighlight();
380
381 return 0;
382 }
383
onSetFocus()384 LRESULT WebInspectorClient::onSetFocus()
385 {
386 SetFocus(m_webViewHwnd);
387 return 0;
388 }
389
onWebViewWindowPosChanging(WPARAM,LPARAM lParam)390 void WebInspectorClient::onWebViewWindowPosChanging(WPARAM, LPARAM lParam)
391 {
392 ASSERT(m_attached);
393
394 WINDOWPOS* windowPos = reinterpret_cast<WINDOWPOS*>(lParam);
395 ASSERT_ARG(lParam, windowPos);
396
397 if (windowPos->flags & SWP_NOSIZE)
398 return;
399
400 windowPos->cy -= defaultAttachedHeight;
401
402 ::SetWindowPos(m_webViewHwnd, 0, windowPos->x, windowPos->y + windowPos->cy, windowPos->cx, defaultAttachedHeight, SWP_NOZORDER);
403 }
404
WebInspectorWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)405 static LRESULT CALLBACK WebInspectorWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
406 {
407 WebInspectorClient* client = reinterpret_cast<WebInspectorClient*>(::GetProp(hwnd, kWebInspectorPointerProp));
408 if (!client)
409 return ::DefWindowProc(hwnd, msg, wParam, lParam);
410
411 switch (msg) {
412 case WM_GETMINMAXINFO:
413 return client->onGetMinMaxInfo(wParam, lParam);
414 case WM_SIZE:
415 return client->onSize(wParam, lParam);
416 case WM_CLOSE:
417 return client->onClose(wParam, lParam);
418 case WM_SETFOCUS:
419 return client->onSetFocus();
420 default:
421 break;
422 }
423
424 return ::DefWindowProc(hwnd, msg, wParam, lParam);
425 }
426
windowReceivedMessage(HWND,UINT msg,WPARAM wParam,LPARAM lParam)427 void WebInspectorClient::windowReceivedMessage(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
428 {
429 switch (msg) {
430 case WM_WINDOWPOSCHANGING:
431 onWebViewWindowPosChanging(wParam, lParam);
432 break;
433 default:
434 break;
435 }
436 }
437
registerWindowClass()438 static ATOM registerWindowClass()
439 {
440 static bool haveRegisteredWindowClass = false;
441
442 if (haveRegisteredWindowClass)
443 return true;
444
445 WNDCLASSEX wcex;
446
447 wcex.cbSize = sizeof(WNDCLASSEX);
448
449 wcex.style = 0;
450 wcex.lpfnWndProc = WebInspectorWndProc;
451 wcex.cbClsExtra = 0;
452 wcex.cbWndExtra = 0;
453 wcex.hInstance = 0;
454 wcex.hIcon = 0;
455 wcex.hCursor = LoadCursor(0, IDC_ARROW);
456 wcex.hbrBackground = 0;
457 wcex.lpszMenuName = 0;
458 wcex.lpszClassName = kWebInspectorWindowClassName;
459 wcex.hIconSm = 0;
460
461 haveRegisteredWindowClass = true;
462
463 return ::RegisterClassEx(&wcex);
464 }
465