4f8e03362c09ca9dbd3055e2d9adc0cd8177fe45
[WebKit-https.git] / Source / WebKit / win / WebCoreSupport / WebInspectorClient.cpp
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     m_frontendPage->inspectorController()->setInspectorFrontendClient(adoptPtr(new WebInspectorFrontendClient(m_inspectedWebView, m_inspectedWebViewHwnd, frontendHwnd, frontendWebView, frontendWebViewHwnd, this, createFrontendSettings())));
175     m_frontendHwnd = frontendHwnd;
176 }
177
178 void WebInspectorClient::highlight()
179 {
180     bool creatingHighlight = !m_highlight;
181
182     if (creatingHighlight)
183         m_highlight = adoptPtr(new WebNodeHighlight(m_inspectedWebView));
184
185     if (m_highlight->isShowing())
186         m_highlight->update();
187     else
188         m_highlight->setShowsWhileWebViewIsVisible(true);
189
190     if (creatingHighlight && IsWindowVisible(m_frontendHwnd))
191         m_highlight->placeBehindWindow(m_frontendHwnd);
192 }
193
194 void WebInspectorClient::hideHighlight()
195 {
196     if (m_highlight)
197         m_highlight->setShowsWhileWebViewIsVisible(false);
198 }
199
200 void WebInspectorClient::updateHighlight()
201 {
202     if (m_highlight && m_highlight->isShowing())
203         m_highlight->update();
204 }
205
206 WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, HWND inspectedWebViewHwnd, HWND frontendHwnd, const COMPtr<WebView>& frontendWebView, HWND frontendWebViewHwnd, WebInspectorClient* inspectorClient, PassOwnPtr<Settings> settings)
207     : InspectorFrontendClientLocal(inspectedWebView->page()->inspectorController(),  core(frontendWebView.get()), settings)
208     , m_inspectedWebView(inspectedWebView)
209     , m_inspectedWebViewHwnd(inspectedWebViewHwnd)
210     , m_inspectorClient(inspectorClient)
211     , m_frontendHwnd(frontendHwnd)
212     , m_frontendWebView(frontendWebView)
213     , m_frontendWebViewHwnd(frontendWebViewHwnd)
214     , m_attached(false)
215     , m_destroyingInspectorView(false)
216 {
217     ::SetProp(frontendHwnd, kWebInspectorPointerProp, reinterpret_cast<HANDLE>(this));
218     // FIXME: Implement window size/position save/restore
219 #if 0
220     [self setWindowFrameAutosaveName:@"Web Inspector"];
221 #endif
222 }
223
224 WebInspectorFrontendClient::~WebInspectorFrontendClient()
225 {
226     destroyInspectorView(true);
227 }
228
229 void WebInspectorFrontendClient::frontendLoaded()
230 {
231     InspectorFrontendClientLocal::frontendLoaded();
232
233     setAttachedWindow(m_attached);
234 }
235
236 String WebInspectorFrontendClient::localizedStringsURL()
237 {
238     RetainPtr<CFURLRef> url(AdoptCF, CFBundleCopyResourceURL(getWebKitBundle(), CFSTR("localizedStrings"), CFSTR("js"), 0));
239     if (!url)
240         return String();
241
242     return CFURLGetString(url.get());
243 }
244
245 String WebInspectorFrontendClient::hiddenPanels()
246 {
247     // FIXME: implement this
248     return String();
249 }
250
251 void WebInspectorFrontendClient::bringToFront()
252 {
253     showWindowWithoutNotifications();
254 }
255
256 void WebInspectorFrontendClient::closeWindow()
257 {
258     destroyInspectorView(true);
259 }
260
261 void WebInspectorFrontendClient::disconnectFromBackend()
262 {
263     destroyInspectorView(false);
264 }
265
266 void WebInspectorFrontendClient::attachWindow()
267 {
268     if (m_attached)
269         return;
270
271     m_inspectorClient->setInspectorStartsAttached(true);
272
273     closeWindowWithoutNotifications();
274     showWindowWithoutNotifications();
275 }
276
277 void WebInspectorFrontendClient::detachWindow()
278 {
279     if (!m_attached)
280         return;
281
282     m_inspectorClient->setInspectorStartsAttached(false);
283
284     closeWindowWithoutNotifications();
285     showWindowWithoutNotifications();
286 }
287
288 void WebInspectorFrontendClient::setAttachedWindowHeight(unsigned height)
289 {
290     if (!m_attached)
291         return;
292
293     HWND hostWindow;
294     if (!SUCCEEDED(m_inspectedWebView->hostWindow((OLE_HANDLE*)&hostWindow)))
295         return;
296
297     RECT hostWindowRect;
298     GetClientRect(hostWindow, &hostWindowRect);
299
300     RECT inspectedRect;
301     GetClientRect(m_inspectedWebViewHwnd, &inspectedRect);
302
303     int totalHeight = hostWindowRect.bottom - hostWindowRect.top;
304     int webViewWidth = inspectedRect.right - inspectedRect.left;
305
306     SetWindowPos(m_frontendWebViewHwnd, 0, 0, totalHeight - height, webViewWidth, height, SWP_NOZORDER);
307
308     // We want to set the inspected web view height to the totalHeight, because the height adjustment
309     // of the inspected web view happens in onWebViewWindowPosChanging, not here.
310     SetWindowPos(m_inspectedWebViewHwnd, 0, 0, 0, webViewWidth, totalHeight, SWP_NOZORDER);
311
312     RedrawWindow(m_frontendWebViewHwnd, 0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW); 
313     RedrawWindow(m_inspectedWebViewHwnd, 0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW);
314 }
315
316 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
317 {
318     m_inspectedURL = newURL;
319     updateWindowTitle();
320 }
321
322 void WebInspectorFrontendClient::closeWindowWithoutNotifications()
323 {
324     if (!m_frontendHwnd)
325         return;
326
327     if (!m_attached) {
328         ShowWindow(m_frontendHwnd, SW_HIDE);
329         return;
330     }
331
332     ASSERT(m_frontendWebView);
333     ASSERT(m_inspectedWebViewHwnd);
334     ASSERT(!IsWindowVisible(m_frontendHwnd));
335
336     // Remove the Inspector's WebView from the inspected WebView's parent window.
337     WindowMessageBroadcaster::removeListener(m_inspectedWebViewHwnd, this);
338
339     m_attached = false;
340
341     m_frontendWebView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_frontendHwnd));
342
343     // Make sure everything has the right size/position.
344     HWND hostWindow;
345     if (SUCCEEDED(m_inspectedWebView->hostWindow((OLE_HANDLE*)&hostWindow)))
346         SendMessage(hostWindow, WM_SIZE, 0, 0);
347 }
348
349 void WebInspectorFrontendClient::showWindowWithoutNotifications()
350 {
351     if (!m_frontendHwnd)
352         return;
353
354     ASSERT(m_frontendWebView);
355     ASSERT(m_inspectedWebViewHwnd);
356
357     bool shouldAttach = false;
358     if (m_attached)
359         shouldAttach = true;
360     else {
361         // If no preference is set - default to an attached window. This is important for inspector LayoutTests.
362         // FIXME: This flag can be fetched directly from the flags storage.
363         shouldAttach = m_inspectorClient->inspectorStartsAttached();
364
365         if (shouldAttach && !canAttachWindow())
366             shouldAttach = false;
367     }
368
369     if (!shouldAttach) {
370         // Put the Inspector's WebView inside our window and show it.
371         m_frontendWebView->setHostWindow(reinterpret_cast<OLE_HANDLE>(m_frontendHwnd));
372         SendMessage(m_frontendHwnd, WM_SIZE, 0, 0);
373         updateWindowTitle();
374
375         SetWindowPos(m_frontendHwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
376         return;
377     }
378
379     // Put the Inspector's WebView inside the inspected WebView's parent window.
380     WindowMessageBroadcaster::addListener(m_inspectedWebViewHwnd, this);
381
382     HWND hostWindow;
383     if (FAILED(m_inspectedWebView->hostWindow(reinterpret_cast<OLE_HANDLE*>(&hostWindow))))
384         return;
385
386     m_frontendWebView->setHostWindow(reinterpret_cast<OLE_HANDLE>(hostWindow));
387
388     // Then hide our own window.
389     ShowWindow(m_frontendHwnd, SW_HIDE);
390
391     m_attached = true;
392
393     // Make sure everything has the right size/position.
394     SendMessage(hostWindow, WM_SIZE, 0, 0);
395     m_inspectorClient->updateHighlight();
396 }
397
398 void WebInspectorFrontendClient::destroyInspectorView(bool notifyInspectorController)
399 {
400     if (m_destroyingInspectorView)
401         return;
402     m_destroyingInspectorView = true;
403
404
405     closeWindowWithoutNotifications();
406
407     if (notifyInspectorController) {
408         m_inspectedWebView->page()->inspectorController()->disconnectFrontend();
409         m_inspectorClient->updateHighlight();
410         m_inspectorClient->frontendClosing();
411     }
412     ::DestroyWindow(m_frontendHwnd);
413 }
414
415 void WebInspectorFrontendClient::updateWindowTitle()
416 {
417     String title = makeString("Web Inspector ", static_cast<UChar>(0x2014), ' ', m_inspectedURL);
418     ::SetWindowText(m_frontendHwnd, title.charactersWithNullTermination());
419 }
420
421 LRESULT WebInspectorFrontendClient::onGetMinMaxInfo(WPARAM, LPARAM lParam)
422 {
423     MINMAXINFO* info = reinterpret_cast<MINMAXINFO*>(lParam);
424     POINT size = {400, 400};
425     info->ptMinTrackSize = size;
426
427     return 0;
428 }
429
430 LRESULT WebInspectorFrontendClient::onSize(WPARAM, LPARAM)
431 {
432     RECT rect;
433     ::GetClientRect(m_frontendHwnd, &rect);
434
435     ::SetWindowPos(m_frontendWebViewHwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
436
437     return 0;
438 }
439
440 LRESULT WebInspectorFrontendClient::onClose(WPARAM, LPARAM)
441 {
442     ::ShowWindow(m_frontendHwnd, SW_HIDE);
443     m_inspectedWebView->page()->inspectorController()->close();
444
445     return 0;
446 }
447
448 LRESULT WebInspectorFrontendClient::onSetFocus()
449 {
450     SetFocus(m_frontendWebViewHwnd);
451     return 0;
452 }
453
454 void WebInspectorFrontendClient::onWebViewWindowPosChanging(WPARAM, LPARAM lParam)
455 {
456     ASSERT(m_attached);
457
458     WINDOWPOS* windowPos = reinterpret_cast<WINDOWPOS*>(lParam);
459     ASSERT_ARG(lParam, windowPos);
460
461     if (windowPos->flags & SWP_NOSIZE)
462         return;
463
464     RECT inspectorRect;
465     GetClientRect(m_frontendWebViewHwnd, &inspectorRect);
466     unsigned inspectorHeight = inspectorRect.bottom - inspectorRect.top;
467
468     windowPos->cy -= inspectorHeight;
469
470     SetWindowPos(m_frontendWebViewHwnd, 0, windowPos->x, windowPos->y + windowPos->cy, windowPos->cx, inspectorHeight, SWP_NOZORDER);
471 }
472
473 static LRESULT CALLBACK WebInspectorWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
474 {
475     WebInspectorFrontendClient* client = reinterpret_cast<WebInspectorFrontendClient*>(::GetProp(hwnd, kWebInspectorPointerProp));
476     if (!client)
477         return ::DefWindowProc(hwnd, msg, wParam, lParam);
478
479     switch (msg) {
480         case WM_GETMINMAXINFO:
481             return client->onGetMinMaxInfo(wParam, lParam);
482         case WM_SIZE:
483             return client->onSize(wParam, lParam);
484         case WM_CLOSE:
485             return client->onClose(wParam, lParam);
486         case WM_SETFOCUS:
487             return client->onSetFocus();
488         default:
489             break;
490     }
491
492     return ::DefWindowProc(hwnd, msg, wParam, lParam);
493 }
494
495 void WebInspectorFrontendClient::windowReceivedMessage(HWND, UINT msg, WPARAM wParam, LPARAM lParam)
496 {
497     switch (msg) {
498         case WM_WINDOWPOSCHANGING:
499             onWebViewWindowPosChanging(wParam, lParam);
500             break;
501         default:
502             break;
503     }
504 }
505
506 static ATOM registerWindowClass()
507 {
508     static bool haveRegisteredWindowClass = false;
509
510     if (haveRegisteredWindowClass)
511         return true;
512
513     WNDCLASSEX wcex;
514
515     wcex.cbSize = sizeof(WNDCLASSEX);
516
517     wcex.style          = 0;
518     wcex.lpfnWndProc    = WebInspectorWndProc;
519     wcex.cbClsExtra     = 0;
520     wcex.cbWndExtra     = 0;
521     wcex.hInstance      = 0;
522     wcex.hIcon          = 0;
523     wcex.hCursor        = LoadCursor(0, IDC_ARROW);
524     wcex.hbrBackground  = 0;
525     wcex.lpszMenuName   = 0;
526     wcex.lpszClassName  = kWebInspectorWindowClassName;
527     wcex.hIconSm        = 0;
528
529     haveRegisteredWindowClass = true;
530
531     return ::RegisterClassEx(&wcex);
532 }