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