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 | |
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 , 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 (WKPageRef , WKArrayRef , WKArrayRef* , WKHitTestResultRef, WKTypeRef, const void*) |
113 | { |
114 | WKMutableArrayRef = WKMutableArrayCreate(); |
115 | |
116 | size_t count = WKArrayGetSize(proposedMenuRef); |
117 | for (size_t i = 0; i < count; ++i) { |
118 | WKContextMenuItemRef = 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 = { |
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 WebCore::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::platformSetSheetRect(const WebCore::FloatRect&) |
448 | { |
449 | notImplemented(); |
450 | } |
451 | |
452 | void WebInspectorProxy::platformStartWindowDrag() |
453 | { |
454 | notImplemented(); |
455 | } |
456 | |
457 | void WebInspectorProxy::platformSave(const String&, const String&, bool, bool) |
458 | { |
459 | notImplemented(); |
460 | } |
461 | |
462 | void WebInspectorProxy::platformAppend(const String&, const String&) |
463 | { |
464 | notImplemented(); |
465 | } |
466 | |
467 | void WebInspectorProxy::platformAttachAvailabilityChanged(bool available) |
468 | { |
469 | if (m_client) |
470 | m_client->didChangeAttachAvailability(*this, available); |
471 | } |
472 | |
473 | } // namespace WebKit |
474 | |