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
50static const float minimumAttachedHeight = 250;
51static const float maximumAttachedHeightRatio = 0.75;
52static const float minimumAttachedWidth = 500;
53
54namespace WebKit {
55using namespace WebCore;
56
57Ref<WebInspector> WebInspector::create(WebPage* page)
58{
59 return adoptRef(*new WebInspector(page));
60}
61
62WebInspector::WebInspector(WebPage* page)
63 : m_page(page)
64{
65}
66
67WebInspector::~WebInspector()
68{
69 if (m_frontendConnection)
70 m_frontendConnection->invalidate();
71}
72
73void WebInspector::openLocalInspectorFrontend(bool underTest)
74{
75 WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::OpenLocalInspectorFrontend(canAttachWindow(), underTest), m_page->pageID());
76}
77
78void 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
109void 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
125void WebInspector::bringToFront()
126{
127 WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::BringToFront(), m_page->pageID());
128}
129
130void 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
141void WebInspector::show()
142{
143 if (!m_page->corePage())
144 return;
145
146 m_page->corePage()->inspectorController().show();
147}
148
149void 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
161void 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
180void WebInspector::evaluateScriptForTest(const String& script)
181{
182 if (!m_page->corePage())
183 return;
184
185 m_page->corePage()->inspectorController().evaluateForTestInFrontend(script);
186}
187
188void 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
200void 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
212void 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
224void 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
242void WebInspector::startPageProfiling()
243{
244 if (!m_page->corePage())
245 return;
246
247 whenFrontendConnectionEstablished([=] {
248 m_frontendConnection->send(Messages::WebInspectorUI::StartPageProfiling(), 0);
249 });
250}
251
252void WebInspector::stopPageProfiling()
253{
254 if (!m_page->corePage())
255 return;
256
257 whenFrontendConnectionEstablished([=] {
258 m_frontendConnection->send(Messages::WebInspectorUI::StopPageProfiling(), 0);
259 });
260}
261
262void WebInspector::startElementSelection()
263{
264 if (!m_page->corePage())
265 return;
266
267 whenFrontendConnectionEstablished([=] {
268 m_frontendConnection->send(Messages::WebInspectorUI::StartElementSelection(), 0);
269 });
270}
271
272void WebInspector::stopElementSelection()
273{
274 if (!m_page->corePage())
275 return;
276
277 whenFrontendConnectionEstablished([=] {
278 m_frontendConnection->send(Messages::WebInspectorUI::StopElementSelection(), 0);
279 });
280}
281
282void WebInspector::elementSelectionChanged(bool active)
283{
284 WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::ElementSelectionChanged(active), m_page->pageID());
285}
286
287bool 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
307void 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