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