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