1 | /* |
2 | * Copyright (C) 2010, 2014-2018 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 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "WebInspector.h" |
28 | |
29 | #include "WebFrame.h" |
30 | #include "WebInspectorMessages.h" |
31 | #include "WebInspectorProxyMessages.h" |
32 | #include "WebInspectorUIMessages.h" |
33 | #include "WebPage.h" |
34 | #include "WebProcess.h" |
35 | #include <WebCore/Chrome.h> |
36 | #include <WebCore/Document.h> |
37 | #include <WebCore/Frame.h> |
38 | #include <WebCore/FrameLoadRequest.h> |
39 | #include <WebCore/FrameLoader.h> |
40 | #include <WebCore/FrameView.h> |
41 | #include <WebCore/InspectorController.h> |
42 | #include <WebCore/InspectorFrontendClient.h> |
43 | #include <WebCore/InspectorPageAgent.h> |
44 | #include <WebCore/NavigationAction.h> |
45 | #include <WebCore/NotImplemented.h> |
46 | #include <WebCore/Page.h> |
47 | #include <WebCore/ScriptController.h> |
48 | #include <WebCore/WindowFeatures.h> |
49 | |
50 | static const float minimumAttachedHeight = 250; |
51 | static const float maximumAttachedHeightRatio = 0.75; |
52 | static const float minimumAttachedWidth = 500; |
53 | |
54 | namespace WebKit { |
55 | using namespace WebCore; |
56 | |
57 | Ref<WebInspector> WebInspector::create(WebPage* page) |
58 | { |
59 | return adoptRef(*new WebInspector(page)); |
60 | } |
61 | |
62 | WebInspector::WebInspector(WebPage* page) |
63 | : m_page(page) |
64 | { |
65 | } |
66 | |
67 | WebInspector::~WebInspector() |
68 | { |
69 | if (m_frontendConnection) |
70 | m_frontendConnection->invalidate(); |
71 | } |
72 | |
73 | void WebInspector::openLocalInspectorFrontend(bool underTest) |
74 | { |
75 | WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::OpenLocalInspectorFrontend(canAttachWindow(), underTest), m_page->pageID()); |
76 | } |
77 | |
78 | void WebInspector::setFrontendConnection(IPC::Attachment encodedConnectionIdentifier) |
79 | { |
80 | // We might receive multiple updates if this web process got swapped into a WebPageProxy |
81 | // shortly after another process established the connection. |
82 | if (m_frontendConnection) { |
83 | m_frontendConnection->invalidate(); |
84 | m_frontendConnection = nullptr; |
85 | } |
86 | |
87 | #if USE(UNIX_DOMAIN_SOCKETS) |
88 | IPC::Connection::Identifier connectionIdentifier(encodedConnectionIdentifier.releaseFileDescriptor()); |
89 | #elif OS(DARWIN) |
90 | IPC::Connection::Identifier connectionIdentifier(encodedConnectionIdentifier.port()); |
91 | #elif OS(WINDOWS) |
92 | IPC::Connection::Identifier connectionIdentifier(encodedConnectionIdentifier.handle()); |
93 | #else |
94 | notImplemented(); |
95 | return; |
96 | #endif |
97 | |
98 | if (!IPC::Connection::identifierIsValid(connectionIdentifier)) |
99 | return; |
100 | |
101 | m_frontendConnection = IPC::Connection::createClientConnection(connectionIdentifier, *this); |
102 | m_frontendConnection->open(); |
103 | |
104 | for (auto& callback : m_frontendConnectionActions) |
105 | callback(); |
106 | m_frontendConnectionActions.clear(); |
107 | } |
108 | |
109 | void WebInspector::closeFrontendConnection() |
110 | { |
111 | WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::DidClose(), m_page->pageID()); |
112 | |
113 | // If we tried to close the frontend before it was created, then no connection exists yet. |
114 | if (m_frontendConnection) { |
115 | m_frontendConnection->invalidate(); |
116 | m_frontendConnection = nullptr; |
117 | } |
118 | |
119 | m_frontendConnectionActions.clear(); |
120 | |
121 | m_attached = false; |
122 | m_previousCanAttach = false; |
123 | } |
124 | |
125 | void WebInspector::bringToFront() |
126 | { |
127 | WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::BringToFront(), m_page->pageID()); |
128 | } |
129 | |
130 | void WebInspector::whenFrontendConnectionEstablished(Function<void()>&& callback) |
131 | { |
132 | if (m_frontendConnection) { |
133 | callback(); |
134 | return; |
135 | } |
136 | |
137 | m_frontendConnectionActions.append(WTFMove(callback)); |
138 | } |
139 | |
140 | // Called by WebInspector messages |
141 | void WebInspector::show() |
142 | { |
143 | if (!m_page->corePage()) |
144 | return; |
145 | |
146 | m_page->corePage()->inspectorController().show(); |
147 | } |
148 | |
149 | void WebInspector::close() |
150 | { |
151 | if (!m_page->corePage()) |
152 | return; |
153 | |
154 | // Close could be called multiple times during teardown. |
155 | if (!m_frontendConnection) |
156 | return; |
157 | |
158 | closeFrontendConnection(); |
159 | } |
160 | |
161 | void WebInspector::openInNewTab(const String& urlString) |
162 | { |
163 | UserGestureIndicator indicator { ProcessingUserGesture }; |
164 | |
165 | Page* inspectedPage = m_page->corePage(); |
166 | if (!inspectedPage) |
167 | return; |
168 | |
169 | Frame& inspectedMainFrame = inspectedPage->mainFrame(); |
170 | FrameLoadRequest frameLoadRequest { *inspectedMainFrame.document(), inspectedMainFrame.document()->securityOrigin(), { urlString }, "_blank"_s , LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown }; |
171 | |
172 | NavigationAction action { *inspectedMainFrame.document(), frameLoadRequest.resourceRequest(), frameLoadRequest.initiatedByMainFrame(), NavigationType::LinkClicked }; |
173 | Page* newPage = inspectedPage->chrome().createWindow(inspectedMainFrame, frameLoadRequest, { }, action); |
174 | if (!newPage) |
175 | return; |
176 | |
177 | newPage->mainFrame().loader().load(WTFMove(frameLoadRequest)); |
178 | } |
179 | |
180 | void WebInspector::evaluateScriptForTest(const String& script) |
181 | { |
182 | if (!m_page->corePage()) |
183 | return; |
184 | |
185 | m_page->corePage()->inspectorController().evaluateForTestInFrontend(script); |
186 | } |
187 | |
188 | void WebInspector::showConsole() |
189 | { |
190 | if (!m_page->corePage()) |
191 | return; |
192 | |
193 | m_page->corePage()->inspectorController().show(); |
194 | |
195 | whenFrontendConnectionEstablished([=] { |
196 | m_frontendConnection->send(Messages::WebInspectorUI::ShowConsole(), 0); |
197 | }); |
198 | } |
199 | |
200 | void WebInspector::showResources() |
201 | { |
202 | if (!m_page->corePage()) |
203 | return; |
204 | |
205 | m_page->corePage()->inspectorController().show(); |
206 | |
207 | whenFrontendConnectionEstablished([=] { |
208 | m_frontendConnection->send(Messages::WebInspectorUI::ShowResources(), 0); |
209 | }); |
210 | } |
211 | |
212 | void WebInspector::showTimelines() |
213 | { |
214 | if (!m_page->corePage()) |
215 | return; |
216 | |
217 | m_page->corePage()->inspectorController().show(); |
218 | |
219 | whenFrontendConnectionEstablished([=] { |
220 | m_frontendConnection->send(Messages::WebInspectorUI::ShowTimelines(), 0); |
221 | }); |
222 | } |
223 | |
224 | void WebInspector::showMainResourceForFrame(uint64_t frameIdentifier) |
225 | { |
226 | WebFrame* frame = WebProcess::singleton().webFrame(frameIdentifier); |
227 | if (!frame) |
228 | return; |
229 | |
230 | if (!m_page->corePage()) |
231 | return; |
232 | |
233 | m_page->corePage()->inspectorController().show(); |
234 | |
235 | String inspectorFrameIdentifier = m_page->corePage()->inspectorController().ensurePageAgent().frameId(frame->coreFrame()); |
236 | |
237 | whenFrontendConnectionEstablished([=] { |
238 | m_frontendConnection->send(Messages::WebInspectorUI::ShowMainResourceForFrame(inspectorFrameIdentifier), 0); |
239 | }); |
240 | } |
241 | |
242 | void WebInspector::startPageProfiling() |
243 | { |
244 | if (!m_page->corePage()) |
245 | return; |
246 | |
247 | whenFrontendConnectionEstablished([=] { |
248 | m_frontendConnection->send(Messages::WebInspectorUI::StartPageProfiling(), 0); |
249 | }); |
250 | } |
251 | |
252 | void WebInspector::stopPageProfiling() |
253 | { |
254 | if (!m_page->corePage()) |
255 | return; |
256 | |
257 | whenFrontendConnectionEstablished([=] { |
258 | m_frontendConnection->send(Messages::WebInspectorUI::StopPageProfiling(), 0); |
259 | }); |
260 | } |
261 | |
262 | void WebInspector::startElementSelection() |
263 | { |
264 | if (!m_page->corePage()) |
265 | return; |
266 | |
267 | whenFrontendConnectionEstablished([=] { |
268 | m_frontendConnection->send(Messages::WebInspectorUI::StartElementSelection(), 0); |
269 | }); |
270 | } |
271 | |
272 | void WebInspector::stopElementSelection() |
273 | { |
274 | if (!m_page->corePage()) |
275 | return; |
276 | |
277 | whenFrontendConnectionEstablished([=] { |
278 | m_frontendConnection->send(Messages::WebInspectorUI::StopElementSelection(), 0); |
279 | }); |
280 | } |
281 | |
282 | void WebInspector::elementSelectionChanged(bool active) |
283 | { |
284 | WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::ElementSelectionChanged(active), m_page->pageID()); |
285 | } |
286 | |
287 | bool WebInspector::canAttachWindow() |
288 | { |
289 | if (!m_page->corePage()) |
290 | return false; |
291 | |
292 | // Don't allow attaching to another inspector -- two inspectors in one window is too much! |
293 | if (m_page->isInspectorPage()) |
294 | return false; |
295 | |
296 | // If we are already attached, allow attaching again to allow switching sides. |
297 | if (m_attached) |
298 | return true; |
299 | |
300 | // Don't allow the attach if the window would be too small to accommodate the minimum inspector size. |
301 | unsigned inspectedPageHeight = m_page->corePage()->mainFrame().view()->visibleHeight(); |
302 | unsigned inspectedPageWidth = m_page->corePage()->mainFrame().view()->visibleWidth(); |
303 | unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio; |
304 | return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth; |
305 | } |
306 | |
307 | void WebInspector::updateDockingAvailability() |
308 | { |
309 | if (m_attached) |
310 | return; |
311 | |
312 | bool canAttachWindow = this->canAttachWindow(); |
313 | if (m_previousCanAttach == canAttachWindow) |
314 | return; |
315 | |
316 | m_previousCanAttach = canAttachWindow; |
317 | |
318 | WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::AttachAvailabilityChanged(canAttachWindow), m_page->pageID()); |
319 | } |
320 | |
321 | } // namespace WebKit |
322 | |