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 61 static const IntRect& defaultWindowRect() 62 { 63 static IntRect rect(60, 200, 750, 650); 64 return rect; 65 } 66 67 static CFBundleRef getWebKitBundle() 68 { 69 return CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit")); 70 } 71 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 89 WebInspectorClient::~WebInspectorClient() 90 { 91 if (m_hwnd) 92 ::DestroyWindow(m_hwnd); 93 } 94 95 void WebInspectorClient::inspectorDestroyed() 96 { 97 delete this; 98 } 99 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 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 208 String WebInspectorClient::hiddenPanels() 209 { 210 // FIXME: implement this 211 return String(); 212 } 213 214 void WebInspectorClient::showWindow() 215 { 216 showWindowWithoutNotifications(); 217 m_inspectedWebView->page()->inspectorController()->setWindowVisible(true, m_shouldAttachWhenShown); 218 } 219 220 void WebInspectorClient::closeWindow() 221 { 222 closeWindowWithoutNotifications(); 223 m_inspectedWebView->page()->inspectorController()->setWindowVisible(false, m_shouldAttachWhenShown); 224 } 225 226 bool WebInspectorClient::windowVisible() 227 { 228 return !!::IsWindowVisible(m_hwnd); 229 } 230 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 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 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 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 297 void WebInspectorClient::hideHighlight() 298 { 299 if (m_highlight) 300 m_highlight->setShowsWhileWebViewIsVisible(false); 301 } 302 303 void WebInspectorClient::inspectedURLChanged(const String& newURL) 304 { 305 m_inspectedURL = newURL; 306 updateWindowTitle(); 307 } 308 309 void WebInspectorClient::inspectorWindowObjectCleared() 310 { 311 notImplemented(); 312 } 313 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 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 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 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 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 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 426 LRESULT WebInspectorClient::onSetFocus() 427 { 428 SetFocus(m_webViewHwnd); 429 return 0; 430 } 431 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 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 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 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