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 <[email protected]>.
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
51namespace WebKit {
52
53static 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
62void WebInspectorProxy::setClient(std::unique_ptr<WebInspectorProxyClient>&& client)
63{
64 m_client = WTFMove(client);
65}
66
67void 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
73static 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
78static 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
85static 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
112static 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
136WebPageProxy* 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
255void 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
276void 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
292void WebInspectorProxy::platformDidCloseForCrash()
293{
294}
295
296void WebInspectorProxy::platformInvalidate()
297{
298}
299
300void WebInspectorProxy::platformHide()
301{
302 notImplemented();
303}
304
305void 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
318void WebInspectorProxy::platformBringInspectedPageToFront()
319{
320 notImplemented();
321}
322
323bool 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
331void 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
341void WebInspectorProxy::platformShowCertificate(const WebCore::CertificateInfo&)
342{
343 notImplemented();
344}
345
346String WebInspectorProxy::inspectorPageURL()
347{
348 return String("resource:///org/webkit/inspector/UserInterface/Main.html");
349}
350
351String WebInspectorProxy::inspectorTestPageURL()
352{
353 return String("resource:///org/webkit/inspector/UserInterface/Test.html");
354}
355
356String WebInspectorProxy::inspectorBaseURL()
357{
358 return String("resource:///org/webkit/inspector/UserInterface/");
359}
360
361unsigned WebInspectorProxy::platformInspectedWindowHeight()
362{
363 return gtk_widget_get_allocated_height(inspectedPage()->viewWidget());
364}
365
366unsigned WebInspectorProxy::platformInspectedWindowWidth()
367{
368 return gtk_widget_get_allocated_width(inspectedPage()->viewWidget());
369}
370
371void 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
400void 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
427void 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
437void 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
447void WebInspectorProxy::platformSetSheetRect(const WebCore::FloatRect&)
448{
449 notImplemented();
450}
451
452void WebInspectorProxy::platformStartWindowDrag()
453{
454 notImplemented();
455}
456
457void WebInspectorProxy::platformSave(const String&, const String&, bool, bool)
458{
459 notImplemented();
460}
461
462void WebInspectorProxy::platformAppend(const String&, const String&)
463{
464 notImplemented();
465}
466
467void WebInspectorProxy::platformAttachAvailabilityChanged(bool available)
468{
469 if (m_client)
470 m_client->didChangeAttachAvailability(*this, available);
471}
472
473} // namespace WebKit
474