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