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