• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
defaultWindowRect()58 static const IntRect& defaultWindowRect()
59 {
60     static IntRect rect(60, 200, 750, 650);
61     return rect;
62 }
63 
getWebKitBundle()64 static CFBundleRef getWebKitBundle()
65 {
66     return CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit"));
67 }
68 
WebInspectorClient(WebView * webView)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 
~WebInspectorClient()77 WebInspectorClient::~WebInspectorClient()
78 {
79     m_frontendPage = 0;
80 }
81 
inspectorDestroyed()82 void WebInspectorClient::inspectorDestroyed()
83 {
84     delete this;
85 }
86 
openInspectorFrontend(InspectorController * inspectorController)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 
highlight(Node *)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 
hideHighlight()195 void WebInspectorClient::hideHighlight()
196 {
197     if (m_highlight)
198         m_highlight->setShowsWhileWebViewIsVisible(false);
199 }
200 
updateHighlight()201 void WebInspectorClient::updateHighlight()
202 {
203     if (m_highlight && m_highlight->isShowing())
204         m_highlight->update();
205 }
206 
WebInspectorFrontendClient(WebView * inspectedWebView,HWND inspectedWebViewHwnd,HWND frontendHwnd,const COMPtr<WebView> & frontendWebView,HWND frontendWebViewHwnd,WebInspectorClient * inspectorClient,PassOwnPtr<Settings> settings)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 
~WebInspectorFrontendClient()225 WebInspectorFrontendClient::~WebInspectorFrontendClient()
226 {
227     destroyInspectorView(true);
228 }
229 
frontendLoaded()230 void WebInspectorFrontendClient::frontendLoaded()
231 {
232     InspectorFrontendClientLocal::frontendLoaded();
233 
234     setAttachedWindow(m_attached);
235 }
236 
localizedStringsURL()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 
hiddenPanels()246 String WebInspectorFrontendClient::hiddenPanels()
247 {
248     // FIXME: implement this
249     return String();
250 }
251 
bringToFront()252 void WebInspectorFrontendClient::bringToFront()
253 {
254     showWindowWithoutNotifications();
255 }
256 
closeWindow()257 void WebInspectorFrontendClient::closeWindow()
258 {
259     destroyInspectorView(true);
260 }
261 
disconnectFromBackend()262 void WebInspectorFrontendClient::disconnectFromBackend()
263 {
264     destroyInspectorView(false);
265 }
266 
attachWindow()267 void WebInspectorFrontendClient::attachWindow()
268 {
269     if (m_attached)
270         return;
271 
272     m_inspectorClient->setInspectorStartsAttached(true);
273 
274     closeWindowWithoutNotifications();
275     showWindowWithoutNotifications();
276 }
277 
detachWindow()278 void WebInspectorFrontendClient::detachWindow()
279 {
280     if (!m_attached)
281         return;
282 
283     m_inspectorClient->setInspectorStartsAttached(false);
284 
285     closeWindowWithoutNotifications();
286     showWindowWithoutNotifications();
287 }
288 
setAttachedWindowHeight(unsigned height)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 
inspectedURLChanged(const String & newURL)317 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
318 {
319     m_inspectedURL = newURL;
320     updateWindowTitle();
321 }
322 
saveSessionSetting(const String & key,const String & value)323 void WebInspectorFrontendClient::saveSessionSetting(const String& key, const String& value)
324 {
325     m_inspectorClient->saveSessionSetting(key, value);
326 }
327 
loadSessionSetting(const String & key,String * value)328 void WebInspectorFrontendClient::loadSessionSetting(const String& key, String* value)
329 {
330     m_inspectorClient->loadSessionSetting(key, value);
331 }
332 
closeWindowWithoutNotifications()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 
showWindowWithoutNotifications()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 
destroyInspectorView(bool notifyInspectorController)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 
updateWindowTitle()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 
onGetMinMaxInfo(WPARAM,LPARAM lParam)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 
onSize(WPARAM,LPARAM)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 
onClose(WPARAM,LPARAM)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 
onSetFocus()459 LRESULT WebInspectorFrontendClient::onSetFocus()
460 {
461     SetFocus(m_frontendWebViewHwnd);
462     return 0;
463 }
464 
onWebViewWindowPosChanging(WPARAM,LPARAM lParam)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 
WebInspectorWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)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 
windowReceivedMessage(HWND,UINT msg,WPARAM wParam,LPARAM lParam)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 
registerWindowClass()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