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