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