Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebKit / UIProcess / API / gtk / WebKitRemoteInspectorProtocolHandler.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2,1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebKitRemoteInspectorProtocolHandler.h"
22
23 #if ENABLE(REMOTE_INSPECTOR)
24
25 #include "APIUserContentWorld.h"
26 #include "WebKitError.h"
27 #include "WebKitNavigationPolicyDecision.h"
28 #include "WebKitUserContentManagerPrivate.h"
29 #include "WebKitWebContextPrivate.h"
30 #include "WebScriptMessageHandler.h"
31 #include <wtf/URL.h>
32
33 namespace WebKit {
34 using namespace WebCore;
35
36 class ScriptMessageClient final : public WebScriptMessageHandler::Client {
37 public:
38     ScriptMessageClient(RemoteInspectorProtocolHandler& inspectorProtocolHandler)
39         : m_inspectorProtocolHandler(inspectorProtocolHandler)
40     {
41     }
42
43     void didPostMessage(WebPageProxy& page, const FrameInfoData&, WebCore::SerializedScriptValue& serializedScriptValue) override
44     {
45         String message = serializedScriptValue.toString();
46         Vector<String> tokens = message.split(':');
47         if (tokens.size() != 2)
48             return;
49
50         URL requestURL = URL({ }, page.pageLoadState().url());
51         m_inspectorProtocolHandler.inspect(requestURL.hostAndPort(), tokens[0].toUInt64(), tokens[1].toUInt64());
52     }
53
54     ~ScriptMessageClient() { }
55
56 private:
57     RemoteInspectorProtocolHandler& m_inspectorProtocolHandler;
58 };
59
60 RemoteInspectorProtocolHandler::RemoteInspectorProtocolHandler(WebKitWebContext* context)
61     : m_context(context)
62 {
63     webkit_web_context_register_uri_scheme(context, "inspector", [](WebKitURISchemeRequest* request, gpointer userData) {
64         static_cast<RemoteInspectorProtocolHandler*>(userData)->handleRequest(request);
65     }, this, nullptr);
66 }
67
68 RemoteInspectorProtocolHandler::~RemoteInspectorProtocolHandler()
69 {
70     for (auto* webView : m_webViews)
71         g_object_weak_unref(G_OBJECT(webView), reinterpret_cast<GWeakNotify>(webViewDestroyed), this);
72
73     for (auto* userContentManager : m_userContentManagers) {
74         webkitUserContentManagerGetUserContentControllerProxy(userContentManager)->removeUserMessageHandlerForName("inspector", API::UserContentWorld::normalWorld());
75         g_object_weak_unref(G_OBJECT(userContentManager), reinterpret_cast<GWeakNotify>(userContentManagerDestroyed), this);
76     }
77 }
78
79 void RemoteInspectorProtocolHandler::webViewDestroyed(RemoteInspectorProtocolHandler* inspectorProtocolHandler, WebKitWebView* webView)
80 {
81     inspectorProtocolHandler->m_webViews.remove(webView);
82 }
83
84 void RemoteInspectorProtocolHandler::userContentManagerDestroyed(RemoteInspectorProtocolHandler* inspectorProtocolHandler, WebKitUserContentManager* userContentManager)
85 {
86     inspectorProtocolHandler->m_userContentManagers.remove(userContentManager);
87 }
88
89 void RemoteInspectorProtocolHandler::handleRequest(WebKitURISchemeRequest* request)
90 {
91     URL requestURL = URL({ }, webkit_uri_scheme_request_get_uri(request));
92     if (!requestURL.port()) {
93         GUniquePtr<GError> error(g_error_new_literal(WEBKIT_POLICY_ERROR, WEBKIT_POLICY_ERROR_CANNOT_SHOW_URI, "Cannot show inspector URL: no port provided"));
94         webkit_uri_scheme_request_finish_error(request, error.get());
95     }
96
97     auto* webView = webkit_uri_scheme_request_get_web_view(request);
98     ASSERT(webView);
99     auto webViewResult = m_webViews.add(webView);
100     if (webViewResult.isNewEntry)
101         g_object_weak_ref(G_OBJECT(webView), reinterpret_cast<GWeakNotify>(webViewDestroyed), this);
102
103     auto* userContentManager = webkit_web_view_get_user_content_manager(webView);
104     auto userContentManagerResult = m_userContentManagers.add(userContentManager);
105     if (userContentManagerResult.isNewEntry) {
106         auto handler = WebScriptMessageHandler::create(std::make_unique<ScriptMessageClient>(*this), "inspector", API::UserContentWorld::normalWorld());
107         webkitUserContentManagerGetUserContentControllerProxy(userContentManager)->addUserScriptMessageHandler(handler.get());
108         g_object_weak_ref(G_OBJECT(userContentManager), reinterpret_cast<GWeakNotify>(userContentManagerDestroyed), this);
109     }
110
111     auto* client = m_inspectorClients.ensure(requestURL.hostAndPort(), [this, &requestURL] {
112         return std::make_unique<RemoteInspectorClient>(requestURL.host().utf8().data(), requestURL.port().value(), *this);
113     }).iterator->value.get();
114
115     GString* html = g_string_new(
116         "<html><head><title>Remote inspector</title>"
117         "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
118         "<style>"
119         "  h1 { color: #babdb6; text-shadow: 0 1px 0 white; margin-bottom: 0; }"
120         "  html { font-family: -webkit-system-font; font-size: 11pt; color: #2e3436; padding: 20px 20px 0 20px; background-color: #f6f6f4; "
121         "         background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #eeeeec), color-stop(1, #f6f6f4));"
122         "         background-size: 100% 5em; background-repeat: no-repeat; }"
123         "  table { width: 100%; border-collapse: collapse; }"
124         "  table, td { border: 1px solid #d3d7cf; border-left: none; border-right: none; }"
125         "  p { margin-bottom: 30px; }"
126         "  td { padding: 15px; }"
127         "  td.data { width: 200px; }"
128         "  .targetname { font-weight: bold; }"
129         "  .targeturl { color: #babdb6; }"
130         "  td.input { width: 64px; }"
131         "  input { width: 100%; padding: 8px; }"
132         "</style>"
133         "</head><body><h1>Inspectable targets</h1>");
134     if (client->targets().isEmpty())
135         g_string_append(html, "<p>No targets found</p>");
136     else {
137         g_string_append(html, "<table>");
138         for (auto connectionID : client->targets().keys()) {
139             for (auto& target : client->targets().get(connectionID)) {
140                 g_string_append_printf(html,
141                     "<tbody><tr>"
142                     "<td class=\"data\"><div class=\"targetname\">%s</div><div class=\"targeturl\">%s</div></td>"
143                     "<td class=\"input\"><input type=\"button\" value=\"Inspect\" onclick=\"window.webkit.messageHandlers.inspector.postMessage('%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT "');\"></td>"
144                     "</tr></tbody>", target.name.data(), target.url.data(), connectionID, target.id);
145             }
146         }
147         g_string_append(html, "</table>");
148     }
149     g_string_append(html, "</body></html>");
150     gsize streamLength = html->len;
151     GRefPtr<GInputStream> stream = adoptGRef(g_memory_input_stream_new_from_data(g_string_free(html, FALSE), streamLength, g_free));
152     webkit_uri_scheme_request_finish(request, stream.get(), streamLength, "text/html");
153 }
154
155 void RemoteInspectorProtocolHandler::inspect(const String& hostAndPort, uint64_t connectionID, uint64_t tatgetID)
156 {
157     if (auto* client = m_inspectorClients.get(hostAndPort))
158         client->inspect(connectionID, tatgetID);
159 }
160
161 void RemoteInspectorProtocolHandler::targetListChanged(RemoteInspectorClient& client)
162 {
163     Vector<WebKitWebView*, 4> webViewsToRemove;
164     for (auto* webView : m_webViews) {
165         if (webkit_web_view_is_loading(webView))
166             continue;
167
168         URL webViewURL = URL({ }, webkit_web_view_get_uri(webView));
169         auto clientForWebView = m_inspectorClients.get(webViewURL.hostAndPort());
170         if (!clientForWebView) {
171             // This view is not showing a inspector view anymore.
172             webViewsToRemove.append(webView);
173         } else if (clientForWebView == &client)
174             webkit_web_view_reload(webView);
175     }
176
177     for (auto* webView : webViewsToRemove) {
178         g_object_weak_unref(G_OBJECT(webView), reinterpret_cast<GWeakNotify>(webViewDestroyed), this);
179         m_webViews.remove(webView);
180     }
181 }
182
183 void RemoteInspectorProtocolHandler::connectionClosed(RemoteInspectorClient& client)
184 {
185     targetListChanged(client);
186     m_inspectorClients.remove(client.hostAndPort());
187 }
188
189 } // namespace WebKit
190
191 #endif // ENABLE(REMOTE_INSPECTOR)