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