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 52using namespace WebCore; 53 54static LPCTSTR kWebInspectorWindowClassName = TEXT("WebInspectorWindowClass"); 55static ATOM registerWindowClass(); 56static LPCTSTR kWebInspectorPointerProp = TEXT("WebInspectorPointer"); 57 58static const IntRect& defaultWindowRect() 59{ 60 static IntRect rect(60, 200, 750, 650); 61 return rect; 62} 63 64static CFBundleRef getWebKitBundle() 65{ 66 return CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit")); 67} 68 69WebInspectorClient::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 77WebInspectorClient::~WebInspectorClient() 78{ 79 m_frontendPage = 0; 80} 81 82void WebInspectorClient::inspectorDestroyed() 83{ 84 delete this; 85} 86 87void 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 179void 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 195void WebInspectorClient::hideHighlight() 196{ 197 if (m_highlight) 198 m_highlight->setShowsWhileWebViewIsVisible(false); 199} 200 201void WebInspectorClient::updateHighlight() 202{ 203 if (m_highlight && m_highlight->isShowing()) 204 m_highlight->update(); 205} 206 207WebInspectorFrontendClient::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 225WebInspectorFrontendClient::~WebInspectorFrontendClient() 226{ 227 destroyInspectorView(true); 228} 229 230void WebInspectorFrontendClient::frontendLoaded() 231{ 232 InspectorFrontendClientLocal::frontendLoaded(); 233 234 setAttachedWindow(m_attached); 235} 236 237String 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 246String WebInspectorFrontendClient::hiddenPanels() 247{ 248 // FIXME: implement this 249 return String(); 250} 251 252void WebInspectorFrontendClient::bringToFront() 253{ 254 showWindowWithoutNotifications(); 255} 256 257void WebInspectorFrontendClient::closeWindow() 258{ 259 destroyInspectorView(true); 260} 261 262void WebInspectorFrontendClient::disconnectFromBackend() 263{ 264 destroyInspectorView(false); 265} 266 267void WebInspectorFrontendClient::attachWindow() 268{ 269 if (m_attached) 270 return; 271 272 m_inspectorClient->setInspectorStartsAttached(true); 273 274 closeWindowWithoutNotifications(); 275 showWindowWithoutNotifications(); 276} 277 278void WebInspectorFrontendClient::detachWindow() 279{ 280 if (!m_attached) 281 return; 282 283 m_inspectorClient->setInspectorStartsAttached(false); 284 285 closeWindowWithoutNotifications(); 286 showWindowWithoutNotifications(); 287} 288 289void 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 317void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL) 318{ 319 m_inspectedURL = newURL; 320 updateWindowTitle(); 321} 322 323void WebInspectorFrontendClient::saveSessionSetting(const String& key, const String& value) 324{ 325 m_inspectorClient->saveSessionSetting(key, value); 326} 327 328void WebInspectorFrontendClient::loadSessionSetting(const String& key, String* value) 329{ 330 m_inspectorClient->loadSessionSetting(key, value); 331} 332 333void 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 360void 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 409void 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 426void WebInspectorFrontendClient::updateWindowTitle() 427{ 428 String title = makeString("Web Inspector ", static_cast<UChar>(0x2014), ' ', m_inspectedURL); 429 ::SetWindowText(m_frontendHwnd, title.charactersWithNullTermination()); 430} 431 432LRESULT 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 441LRESULT 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 451LRESULT WebInspectorFrontendClient::onClose(WPARAM, LPARAM) 452{ 453 ::ShowWindow(m_frontendHwnd, SW_HIDE); 454 m_inspectedWebView->page()->inspectorController()->close(); 455 456 return 0; 457} 458 459LRESULT WebInspectorFrontendClient::onSetFocus() 460{ 461 SetFocus(m_frontendWebViewHwnd); 462 return 0; 463} 464 465void 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 484static 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 506void 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 517static 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