• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
defaultWindowRect()61 static const IntRect& defaultWindowRect()
62 {
63     static IntRect rect(60, 200, 750, 650);
64     return rect;
65 }
66 
getWebKitBundle()67 static CFBundleRef getWebKitBundle()
68 {
69     return CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
70 }
71 
WebInspectorClient(WebView * webView)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 
~WebInspectorClient()89 WebInspectorClient::~WebInspectorClient()
90 {
91     if (m_hwnd)
92         ::DestroyWindow(m_hwnd);
93 }
94 
inspectorDestroyed()95 void WebInspectorClient::inspectorDestroyed()
96 {
97     delete this;
98 }
99 
createPage()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 
localizedStringsURL()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 
hiddenPanels()208 String WebInspectorClient::hiddenPanels()
209 {
210     // FIXME: implement this
211     return String();
212 }
213 
showWindow()214 void WebInspectorClient::showWindow()
215 {
216     showWindowWithoutNotifications();
217     m_inspectedWebView->page()->inspectorController()->setWindowVisible(true, m_shouldAttachWhenShown);
218 }
219 
closeWindow()220 void WebInspectorClient::closeWindow()
221 {
222     closeWindowWithoutNotifications();
223     m_inspectedWebView->page()->inspectorController()->setWindowVisible(false, m_shouldAttachWhenShown);
224 }
225 
windowVisible()226 bool WebInspectorClient::windowVisible()
227 {
228     return !!::IsWindowVisible(m_hwnd);
229 }
230 
attachWindow()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 
detachWindow()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 
setAttachedWindowHeight(unsigned height)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 
highlight(Node *)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 
hideHighlight()297 void WebInspectorClient::hideHighlight()
298 {
299     if (m_highlight)
300         m_highlight->setShowsWhileWebViewIsVisible(false);
301 }
302 
inspectedURLChanged(const String & newURL)303 void WebInspectorClient::inspectedURLChanged(const String& newURL)
304 {
305     m_inspectedURL = newURL;
306     updateWindowTitle();
307 }
308 
inspectorWindowObjectCleared()309 void WebInspectorClient::inspectorWindowObjectCleared()
310 {
311     notImplemented();
312 }
313 
closeWindowWithoutNotifications()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 
showWindowWithoutNotifications()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 
updateWindowTitle()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 
onGetMinMaxInfo(WPARAM,LPARAM lParam)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 
onSize(WPARAM,LPARAM)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 
onClose(WPARAM,LPARAM)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 
onSetFocus()426 LRESULT WebInspectorClient::onSetFocus()
427 {
428     SetFocus(m_webViewHwnd);
429     return 0;
430 }
431 
onWebViewWindowPosChanging(WPARAM,LPARAM lParam)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 
WebInspectorWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)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 
windowReceivedMessage(HWND,UINT msg,WPARAM wParam,LPARAM lParam)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 
registerWindowClass()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