Delay WebProcess launch until a load is triggered in a Web view
[WebKit-https.git] / Source / WebKit / UIProcess / gtk / WebInspectorProxyGtk.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
4  * Copyright (C) 2012 Igalia S.L.
5  * Copyright (C) 2013 Gustavo Noronha Silva <gns@gnome.org>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "WebInspectorProxy.h"
31
32 #include "APINavigation.h"
33 #include "APINavigationAction.h"
34 #include "WKArray.h"
35 #include "WKContextMenuItem.h"
36 #include "WKMutableArray.h"
37 #include "WebFramePolicyListenerProxy.h"
38 #include "WebInspectorProxyClient.h"
39 #include "WebKitInspectorWindow.h"
40 #include "WebKitWebViewBasePrivate.h"
41 #include "WebPageGroup.h"
42 #include "WebProcessPool.h"
43 #include "WebProcessProxy.h"
44 #include <WebCore/CertificateInfo.h>
45 #include <WebCore/GtkUtilities.h>
46 #include <WebCore/NotImplemented.h>
47 #include <wtf/FileSystem.h>
48 #include <wtf/text/CString.h>
49 #include <wtf/text/WTFString.h>
50
51 namespace WebKit {
52
53 static void inspectorViewDestroyed(GtkWidget*, gpointer userData)
54 {
55     WebInspectorProxy* inspectorProxy = static_cast<WebInspectorProxy*>(userData);
56
57     // Inform WebProcess about webinspector closure. Not doing so,
58     // results in failure of subsequent invocation of webinspector.
59     inspectorProxy->close();
60 }
61
62 void WebInspectorProxy::setClient(std::unique_ptr<WebInspectorProxyClient>&& client)
63 {
64     m_client = WTFMove(client);
65 }
66
67 void WebInspectorProxy::updateInspectorWindowTitle() const
68 {
69     ASSERT(m_inspectorWindow);
70     webkitInspectorWindowSetSubtitle(WEBKIT_INSPECTOR_WINDOW(m_inspectorWindow), !m_inspectedURLString.isEmpty() ? m_inspectedURLString.utf8().data() : nullptr);
71 }
72
73 static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long currentDatabaseUsage, unsigned long long expectedUsage, const void*)
74 {
75     return std::max<unsigned long long>(expectedUsage, currentDatabaseUsage * 1.25);
76 }
77
78 static void webProcessDidCrash(WKPageRef, const void* clientInfo)
79 {
80     WebInspectorProxy* webInspectorProxy = static_cast<WebInspectorProxy*>(const_cast<void*>(clientInfo));
81     ASSERT(webInspectorProxy);
82     webInspectorProxy->closeForCrash();
83 }
84
85 static void decidePolicyForNavigationAction(WKPageRef pageRef, WKNavigationActionRef navigationActionRef, WKFramePolicyListenerRef listenerRef, WKTypeRef, const void* clientInfo)
86 {
87     // Allow non-main frames to navigate anywhere.
88     API::FrameInfo* sourceFrame = toImpl(navigationActionRef)->sourceFrame();
89     if (sourceFrame && !sourceFrame->isMainFrame()) {
90         toImpl(listenerRef)->use({ });
91         return;
92     }
93
94     const WebInspectorProxy* webInspectorProxy = static_cast<const WebInspectorProxy*>(clientInfo);
95     ASSERT(webInspectorProxy);
96
97     WebCore::ResourceRequest request = toImpl(navigationActionRef)->request();
98
99     // Allow loading of the main inspector file.
100     if (WebInspectorProxy::isMainOrTestInspectorPage(request.url())) {
101         toImpl(listenerRef)->use({ });
102         return;
103     }
104
105     // Prevent everything else from loading in the inspector's page.
106     toImpl(listenerRef)->ignore();
107
108     // And instead load it in the inspected page.
109     webInspectorProxy->inspectedPage()->loadRequest(WTFMove(request));
110 }
111
112 static void getContextMenuFromProposedMenu(WKPageRef pageRef, WKArrayRef proposedMenuRef, WKArrayRef* newMenuRef, WKHitTestResultRef, WKTypeRef, const void*)
113 {
114     WKMutableArrayRef menuItems = WKMutableArrayCreate();
115
116     size_t count = WKArrayGetSize(proposedMenuRef);
117     for (size_t i = 0; i < count; ++i) {
118         WKContextMenuItemRef contextMenuItem = static_cast<WKContextMenuItemRef>(WKArrayGetItemAtIndex(proposedMenuRef, i));
119         switch (WKContextMenuItemGetTag(contextMenuItem)) {
120         case kWKContextMenuItemTagOpenLinkInNewWindow:
121         case kWKContextMenuItemTagOpenImageInNewWindow:
122         case kWKContextMenuItemTagOpenFrameInNewWindow:
123         case kWKContextMenuItemTagOpenMediaInNewWindow:
124         case kWKContextMenuItemTagDownloadLinkToDisk:
125         case kWKContextMenuItemTagDownloadImageToDisk:
126             break;
127         default:
128             WKArrayAppendItem(menuItems, contextMenuItem);
129             break;
130         }
131     }
132
133     *newMenuRef = menuItems;
134 }
135
136 WebPageProxy* WebInspectorProxy::platformCreateFrontendPage()
137 {
138     ASSERT(inspectedPage());
139     ASSERT(!m_inspectorView);
140
141     auto preferences = WebPreferences::create(String(), "WebKit2.", "WebKit2.");
142 #if ENABLE(DEVELOPER_MODE)
143     // Allow developers to inspect the Web Inspector in debug builds without changing settings.
144     preferences->setDeveloperExtrasEnabled(true);
145     preferences->setLogsPageMessagesToSystemConsoleEnabled(true);
146 #endif
147     preferences->setJavaScriptRuntimeFlags({
148     });
149     auto pageGroup = WebPageGroup::create(inspectorPageGroupIdentifierForPage(inspectedPage()));
150
151     auto pageConfiguration = API::PageConfiguration::create();
152     pageConfiguration->setProcessPool(&inspectorProcessPool(inspectionLevel()));
153     pageConfiguration->setPreferences(preferences.ptr());
154     pageConfiguration->setPageGroup(pageGroup.ptr());
155     m_inspectorView = GTK_WIDGET(webkitWebViewBaseCreate(*pageConfiguration.ptr()));
156     g_object_add_weak_pointer(G_OBJECT(m_inspectorView), reinterpret_cast<void**>(&m_inspectorView));
157     g_signal_connect(m_inspectorView, "destroy", G_CALLBACK(inspectorViewDestroyed), this);
158
159     WKPageUIClientV2 uiClient = {
160         { 2, this },
161         nullptr, // createNewPage_deprecatedForUseWithV0
162         nullptr, // showPage
163         nullptr, // closePage
164         nullptr, // takeFocus
165         nullptr, // focus
166         nullptr, // unfocus
167         nullptr, // runJavaScriptAlert
168         nullptr, // runJavaScriptConfirm
169         nullptr, // runJavaScriptPrompt
170         nullptr, // setStatusText
171         nullptr, // mouseDidMoveOverElement_deprecatedForUseWithV0
172         nullptr, // missingPluginButtonClicked_deprecatedForUseWithV0
173         nullptr, // didNotHandleKeyEvent
174         nullptr, // didNotHandleWheelEvent
175         nullptr, // areToolbarsVisible
176         nullptr, // setToolbarsVisible
177         nullptr, // isMenuBarVisible
178         nullptr, // setMenuBarVisible
179         nullptr, // isStatusBarVisible
180         nullptr, // setStatusBarVisible
181         nullptr, // isResizable
182         nullptr, // setResizable
183         nullptr, // getWindowFrame,
184         nullptr, // setWindowFrame,
185         nullptr, // runBeforeUnloadConfirmPanel
186         nullptr, // didDraw
187         nullptr, // pageDidScroll
188         exceededDatabaseQuota,
189         nullptr, // runOpenPanel,
190         nullptr, // decidePolicyForGeolocationPermissionRequest
191         nullptr, // headerHeight
192         nullptr, // footerHeight
193         nullptr, // drawHeader
194         nullptr, // drawFooter
195         nullptr, // printFrame
196         nullptr, // runModal
197         nullptr, // unused
198         nullptr, // saveDataToFileInDownloadsFolder
199         nullptr, // shouldInterruptJavaScript
200         nullptr, // createPage
201         nullptr, // mouseDidMoveOverElement
202         nullptr, // decidePolicyForNotificationPermissionRequest
203         nullptr, // unavailablePluginButtonClicked_deprecatedForUseWithV1
204         nullptr, // showColorPicker
205         nullptr, // hideColorPicker
206         nullptr, // unavailablePluginButtonClicked
207     };
208
209     WKPageNavigationClientV0 navigationClient = {
210         { 0, this },
211         decidePolicyForNavigationAction,
212         nullptr, // decidePolicyForNavigationResponse
213         nullptr, // decidePolicyForPluginLoad
214         nullptr, // didStartProvisionalNavigation
215         nullptr, // didReceiveServerRedirectForProvisionalNavigation
216         nullptr, // didFailProvisionalNavigation
217         nullptr, // didCommitNavigation
218         nullptr, // didFinishNavigation
219         nullptr, // didFailNavigation
220         nullptr, // didFailProvisionalLoadInSubframe
221         nullptr, // didFinishDocumentLoad
222         nullptr, // didSameDocumentNavigation
223         nullptr, // renderingProgressDidChange
224         nullptr, // canAuthenticateAgainstProtectionSpace
225         nullptr, // didReceiveAuthenticationChallenge
226         webProcessDidCrash,
227         nullptr, // copyWebCryptoMasterKey
228
229         nullptr, // didBeginNavigationGesture
230         nullptr, // willEndNavigationGesture
231         nullptr, // didEndNavigationGesture
232         nullptr, // didRemoveNavigationGestureSnapshot
233     };
234
235     WKPageContextMenuClientV3 contextMenuClient = {
236         { 3, this },
237         nullptr, // getContextMenuFromProposedMenu_deprecatedForUseWithV0
238         nullptr, // customContextMenuItemSelected
239         nullptr, // contextMenuDismissed
240         getContextMenuFromProposedMenu,
241         nullptr, // showContextMenu
242         nullptr, // hideContextMenu
243     };
244
245     WebPageProxy* inspectorPage = webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(m_inspectorView));
246     ASSERT(inspectorPage);
247
248     WKPageSetPageUIClient(toAPI(inspectorPage), &uiClient.base);
249     WKPageSetPageNavigationClient(toAPI(inspectorPage), &navigationClient.base);
250     WKPageSetPageContextMenuClient(toAPI(inspectorPage), &contextMenuClient.base);
251
252     return inspectorPage;
253 }
254
255 void WebInspectorProxy::platformCreateFrontendWindow()
256 {
257     if (m_client && m_client->openWindow(*this))
258         return;
259
260     GtkWidget* inspectedViewParent = gtk_widget_get_toplevel(inspectedPage()->viewWidget());
261     if (!WebCore::widgetIsOnscreenToplevelWindow(inspectedViewParent))
262         inspectedViewParent = nullptr;
263
264     ASSERT(!m_inspectorWindow);
265     m_inspectorWindow = webkitInspectorWindowNew(inspectedViewParent ? GTK_WINDOW(inspectedViewParent) : nullptr);
266     gtk_container_add(GTK_CONTAINER(m_inspectorWindow), m_inspectorView);
267     gtk_widget_show(m_inspectorView);
268
269     if (!m_inspectedURLString.isEmpty())
270         updateInspectorWindowTitle();
271
272     g_object_add_weak_pointer(G_OBJECT(m_inspectorWindow), reinterpret_cast<void**>(&m_inspectorWindow));
273     gtk_window_present(GTK_WINDOW(m_inspectorWindow));
274 }
275
276 void WebInspectorProxy::platformCloseFrontendPageAndWindow()
277 {
278     if (m_inspectorView) {
279         g_signal_handlers_disconnect_by_func(m_inspectorView, reinterpret_cast<void*>(inspectorViewDestroyed), this);
280         m_inspectorView = nullptr;
281     }
282
283     if (m_client)
284         m_client->didClose(*this);
285
286     if (m_inspectorWindow) {
287         gtk_widget_destroy(m_inspectorWindow);
288         m_inspectorWindow = nullptr;
289     }
290 }
291
292 void WebInspectorProxy::platformDidCloseForCrash()
293 {
294 }
295
296 void WebInspectorProxy::platformInvalidate()
297 {
298 }
299
300 void WebInspectorProxy::platformHide()
301 {
302     notImplemented();
303 }
304
305 void WebInspectorProxy::platformBringToFront()
306 {
307     if (m_isOpening)
308         return;
309
310     if (m_client && m_client->bringToFront(*this))
311         return;
312
313     GtkWidget* parent = gtk_widget_get_toplevel(m_inspectorView);
314     if (WebCore::widgetIsOnscreenToplevelWindow(parent))
315         gtk_window_present(GTK_WINDOW(parent));
316 }
317
318 void WebInspectorProxy::platformBringInspectedPageToFront()
319 {
320     notImplemented();
321 }
322
323 bool WebInspectorProxy::platformIsFront()
324 {
325     GtkWidget* parent = gtk_widget_get_toplevel(m_inspectorView);
326     if (WebCore::widgetIsOnscreenToplevelWindow(parent))
327         return m_isVisible && gtk_window_is_active(GTK_WINDOW(parent));
328     return false;
329 }
330
331 void WebInspectorProxy::platformInspectedURLChanged(const String& url)
332 {
333     m_inspectedURLString = url;
334     if (m_client)
335         m_client->inspectedURLChanged(*this, url);
336
337     if (m_inspectorWindow)
338         updateInspectorWindowTitle();
339 }
340
341 void WebInspectorProxy::platformShowCertificate(const CertificateInfo&)
342 {
343     notImplemented();
344 }
345
346 String WebInspectorProxy::inspectorPageURL()
347 {
348     return String("resource:///org/webkit/inspector/UserInterface/Main.html");
349 }
350
351 String WebInspectorProxy::inspectorTestPageURL()
352 {
353     return String("resource:///org/webkit/inspector/UserInterface/Test.html");
354 }
355
356 String WebInspectorProxy::inspectorBaseURL()
357 {
358     return String("resource:///org/webkit/inspector/UserInterface/");
359 }
360
361 unsigned WebInspectorProxy::platformInspectedWindowHeight()
362 {
363     return gtk_widget_get_allocated_height(inspectedPage()->viewWidget());
364 }
365
366 unsigned WebInspectorProxy::platformInspectedWindowWidth()
367 {
368     return gtk_widget_get_allocated_width(inspectedPage()->viewWidget());
369 }
370
371 void WebInspectorProxy::platformAttach()
372 {
373     GRefPtr<GtkWidget> inspectorView = m_inspectorView;
374     if (m_inspectorWindow) {
375         gtk_container_remove(GTK_CONTAINER(m_inspectorWindow), m_inspectorView);
376         gtk_widget_destroy(m_inspectorWindow);
377         m_inspectorWindow = 0;
378     }
379
380     // Set a default sizes based on InspectorFrontendClientLocal.
381     static const unsigned defaultAttachedSize = 300;
382     static const unsigned minimumAttachedWidth = 750;
383     static const unsigned minimumAttachedHeight = 250;
384
385     if (m_attachmentSide == AttachmentSide::Bottom) {
386         unsigned maximumAttachedHeight = platformInspectedWindowHeight() * 3 / 4;
387         platformSetAttachedWindowHeight(std::max(minimumAttachedHeight, std::min(defaultAttachedSize, maximumAttachedHeight)));
388     } else {
389         unsigned maximumAttachedWidth = platformInspectedWindowWidth() * 3 / 4;
390         platformSetAttachedWindowWidth(std::max(minimumAttachedWidth, std::min(defaultAttachedSize, maximumAttachedWidth)));
391     }
392
393     if (m_client && m_client->attach(*this))
394         return;
395
396     webkitWebViewBaseAddWebInspector(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), m_inspectorView, m_attachmentSide);
397     gtk_widget_show(m_inspectorView);
398 }
399
400 void WebInspectorProxy::platformDetach()
401 {
402     if (!inspectedPage()->hasRunningProcess())
403         return;
404
405     GRefPtr<GtkWidget> inspectorView = m_inspectorView;
406     if (!m_client || !m_client->detach(*this)) {
407         // Detach is called when m_isAttached is true, but it could called before
408         // the inspector is opened if the inspector is shown/closed quickly. So,
409         // we might not have a parent yet.
410         if (GtkWidget* parent = gtk_widget_get_parent(m_inspectorView))
411             gtk_container_remove(GTK_CONTAINER(parent), m_inspectorView);
412     }
413
414     // Return early if we are not visible. This means the inspector was closed while attached
415     // and we should not create and show the inspector window.
416     if (!m_isVisible) {
417         // The inspector view will be destroyed, but we don't need to notify the web process to close the
418         // inspector in this case, since it's already closed.
419         g_signal_handlers_disconnect_by_func(m_inspectorView, reinterpret_cast<void*>(inspectorViewDestroyed), this);
420         m_inspectorView = nullptr;
421         return;
422     }
423
424     open();
425 }
426
427 void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned height)
428 {
429     if (!m_isAttached)
430         return;
431
432     if (m_client)
433         m_client->didChangeAttachedHeight(*this, height);
434     webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), height);
435 }
436
437 void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned width)
438 {
439     if (!m_isAttached)
440         return;
441
442     if (m_client)
443         m_client->didChangeAttachedWidth(*this, width);
444     webkitWebViewBaseSetInspectorViewSize(WEBKIT_WEB_VIEW_BASE(inspectedPage()->viewWidget()), width);
445 }
446
447 void WebInspectorProxy::platformStartWindowDrag()
448 {
449     notImplemented();
450 }
451
452 void WebInspectorProxy::platformSave(const String&, const String&, bool, bool)
453 {
454     notImplemented();
455 }
456
457 void WebInspectorProxy::platformAppend(const String&, const String&)
458 {
459     notImplemented();
460 }
461
462 void WebInspectorProxy::platformAttachAvailabilityChanged(bool available)
463 {
464     if (m_client)
465         m_client->didChangeAttachAvailability(*this, available);
466 }
467
468 } // namespace WebKit