Home | History | Annotate | Download | only in WebCoreSupport
      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