Web Inspector: consolidate code that hosts the Inspector page inside a WKWebView
[WebKit-https.git] / Source / WebKit / UIProcess / WebInspectorProxy.cpp
1 /*
2  * Copyright (C) 2010-2017 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2011 Motorola Mobility, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "WebInspectorProxy.h"
29
30 #include "APIProcessPoolConfiguration.h"
31 #include "WebAutomationSession.h"
32 #include "WebFrameProxy.h"
33 #include "WebInspectorInterruptDispatcherMessages.h"
34 #include "WebInspectorMessages.h"
35 #include "WebInspectorProxyMessages.h"
36 #include "WebInspectorUIMessages.h"
37 #include "WebPageGroup.h"
38 #include "WebPageProxy.h"
39 #include "WebPreferences.h"
40 #include "WebProcessPool.h"
41 #include "WebProcessProxy.h"
42 #include <WebCore/NotImplemented.h>
43
44 #if PLATFORM(GTK)
45 #include "WebInspectorProxyClient.h"
46 #endif
47
48 using namespace WebCore;
49
50 namespace WebKit {
51
52 const unsigned WebInspectorProxy::minimumWindowWidth = 500;
53 const unsigned WebInspectorProxy::minimumWindowHeight = 400;
54
55 const unsigned WebInspectorProxy::initialWindowWidth = 1000;
56 const unsigned WebInspectorProxy::initialWindowHeight = 650;
57
58 WebInspectorProxy::WebInspectorProxy(WebPageProxy* inspectedPage)
59     : m_inspectedPage(inspectedPage)
60 #if PLATFORM(MAC) && WK_API_ENABLED
61     , m_closeFrontendAfterInactivityTimer(RunLoop::main(), this, &WebInspectorProxy::closeFrontendAfterInactivityTimerFired)
62 #endif
63 {
64     m_inspectedPage->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID(), *this);
65 }
66
67 WebInspectorProxy::~WebInspectorProxy()
68 {
69 }
70
71 unsigned WebInspectorProxy::inspectionLevel() const
72 {
73     return inspectorLevelForPage(inspectedPage());
74 }
75
76 WebPreferences& WebInspectorProxy::inspectorPagePreferences() const
77 {
78     ASSERT(m_inspectorPage);
79     return m_inspectorPage->pageGroup().preferences();
80 }
81
82 void WebInspectorProxy::invalidate()
83 {
84     m_inspectedPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
85
86     closeFrontendPageAndWindow();
87     platformInvalidate();
88
89     m_inspectedPage = nullptr;
90 }
91
92 // Public APIs
93 bool WebInspectorProxy::isFront()
94 {
95     if (!m_inspectedPage)
96         return false;
97
98     return platformIsFront();
99 }
100
101 void WebInspectorProxy::connect()
102 {
103     if (!m_inspectedPage)
104         return;
105
106     if (m_showMessageSent)
107         return;
108
109     m_showMessageSent = true;
110     m_ignoreFirstBringToFront = true;
111
112     createFrontendPage();
113
114     m_inspectedPage->process().send(Messages::WebInspectorInterruptDispatcher::NotifyNeedDebuggerBreak(), 0);
115     m_inspectedPage->process().send(Messages::WebInspector::Show(), m_inspectedPage->pageID());
116 }
117
118 void WebInspectorProxy::show()
119 {
120     if (!m_inspectedPage)
121         return;
122
123     if (isConnected()) {
124         bringToFront();
125         return;
126     }
127
128     connect();
129
130     // Don't ignore the first bringToFront so it opens the Inspector.
131     m_ignoreFirstBringToFront = false;
132 }
133
134 void WebInspectorProxy::hide()
135 {
136     if (!m_inspectedPage)
137         return;
138
139     m_isVisible = false;
140
141     platformHide();
142 }
143
144 void WebInspectorProxy::close()
145 {
146     if (!m_inspectedPage)
147         return;
148
149     m_inspectedPage->process().send(Messages::WebInspector::Close(), m_inspectedPage->pageID());
150
151     closeFrontendPageAndWindow();
152 }
153
154 void WebInspectorProxy::closeForCrash()
155 {
156     close();
157
158     platformDidCloseForCrash();
159 }
160
161 void WebInspectorProxy::showConsole()
162 {
163     if (!m_inspectedPage)
164         return;
165
166     createFrontendPage();
167
168     m_inspectedPage->process().send(Messages::WebInspector::ShowConsole(), m_inspectedPage->pageID());
169 }
170
171 void WebInspectorProxy::showResources()
172 {
173     if (!m_inspectedPage)
174         return;
175
176     createFrontendPage();
177
178     m_inspectedPage->process().send(Messages::WebInspector::ShowResources(), m_inspectedPage->pageID());
179 }
180
181 void WebInspectorProxy::showTimelines()
182 {
183     if (!m_inspectedPage)
184         return;
185
186     createFrontendPage();
187
188     m_inspectedPage->process().send(Messages::WebInspector::ShowTimelines(), m_inspectedPage->pageID());
189 }
190
191 void WebInspectorProxy::showMainResourceForFrame(WebFrameProxy* frame)
192 {
193     if (!m_inspectedPage)
194         return;
195
196     createFrontendPage();
197
198     m_inspectedPage->process().send(Messages::WebInspector::ShowMainResourceForFrame(frame->frameID()), m_inspectedPage->pageID());
199 }
200
201 void WebInspectorProxy::attachBottom()
202 {
203     attach(AttachmentSide::Bottom);
204 }
205
206 void WebInspectorProxy::attachRight()
207 {
208     attach(AttachmentSide::Right);
209 }
210
211 void WebInspectorProxy::attachLeft()
212 {
213     attach(AttachmentSide::Left);
214 }
215
216 void WebInspectorProxy::attach(AttachmentSide side)
217 {
218     if (!m_inspectedPage || !canAttach())
219         return;
220
221     m_isAttached = true;
222     m_attachmentSide = side;
223
224     inspectorPagePreferences().setInspectorAttachmentSide(static_cast<uint32_t>(side));
225
226     if (m_isVisible)
227         inspectorPagePreferences().setInspectorStartsAttached(true);
228
229     m_inspectedPage->process().send(Messages::WebInspector::SetAttached(true), m_inspectedPage->pageID());
230
231     switch (m_attachmentSide) {
232     case AttachmentSide::Bottom:
233         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedBottom(), m_inspectorPage->pageID());
234         break;
235
236     case AttachmentSide::Right:
237         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedRight(), m_inspectorPage->pageID());
238         break;
239
240     case AttachmentSide::Left:
241         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedLeft(), m_inspectorPage->pageID());
242         break;
243     }
244
245     platformAttach();
246 }
247
248 void WebInspectorProxy::detach()
249 {
250     if (!m_inspectedPage)
251         return;
252
253     m_isAttached = false;
254
255     if (m_isVisible)
256         inspectorPagePreferences().setInspectorStartsAttached(false);
257
258     m_inspectedPage->process().send(Messages::WebInspector::SetAttached(false), m_inspectedPage->pageID());
259     m_inspectorPage->process().send(Messages::WebInspectorUI::Detached(), m_inspectorPage->pageID());
260
261     platformDetach();
262 }
263
264 void WebInspectorProxy::setAttachedWindowHeight(unsigned height)
265 {
266     inspectorPagePreferences().setInspectorAttachedHeight(height);
267     platformSetAttachedWindowHeight(height);
268 }
269
270 void WebInspectorProxy::setAttachedWindowWidth(unsigned width)
271 {
272     inspectorPagePreferences().setInspectorAttachedWidth(width);
273     platformSetAttachedWindowWidth(width);
274 }
275
276 void WebInspectorProxy::startWindowDrag()
277 {
278     platformStartWindowDrag();
279 }
280
281 void WebInspectorProxy::togglePageProfiling()
282 {
283     if (!m_inspectedPage)
284         return;
285
286     if (m_isProfilingPage)
287         m_inspectedPage->process().send(Messages::WebInspector::StopPageProfiling(), m_inspectedPage->pageID());
288     else
289         m_inspectedPage->process().send(Messages::WebInspector::StartPageProfiling(), m_inspectedPage->pageID());
290
291     // FIXME: have the WebProcess notify us on state changes.
292     m_isProfilingPage = !m_isProfilingPage;
293 }
294
295 void WebInspectorProxy::toggleElementSelection()
296 {
297     if (!m_inspectedPage)
298         return;
299
300     if (m_elementSelectionActive) {
301         m_ignoreElementSelectionChange = true;
302         m_inspectedPage->process().send(Messages::WebInspector::StopElementSelection(), m_inspectedPage->pageID());
303     } else {
304         connect();
305         m_inspectedPage->process().send(Messages::WebInspector::StartElementSelection(), m_inspectedPage->pageID());
306     }
307 }
308
309 bool WebInspectorProxy::isMainOrTestInspectorPage(const URL& url)
310 {
311     // Use URL so we can compare the paths and protocols.
312     URL mainPageURL(URL(), WebInspectorProxy::inspectorPageURL());
313     if (url.protocol() == mainPageURL.protocol() && decodeURLEscapeSequences(url.path()) == decodeURLEscapeSequences(mainPageURL.path()))
314         return true;
315
316     // We might not have a Test URL in Production builds.
317     String testPageURLString = WebInspectorProxy::inspectorTestPageURL();
318     if (testPageURLString.isNull())
319         return false;
320
321     URL testPageURL(URL(), testPageURLString);
322     return url.protocol() == testPageURL.protocol() && decodeURLEscapeSequences(url.path()) == decodeURLEscapeSequences(testPageURL.path());
323 }
324
325 void WebInspectorProxy::createFrontendPage()
326 {
327     if (m_inspectorPage)
328         return;
329
330     m_inspectorPage = platformCreateFrontendPage();
331     ASSERT(m_inspectorPage);
332     if (!m_inspectorPage)
333         return;
334
335     trackInspectorPage(m_inspectorPage);
336
337     m_inspectorPage->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID(), *this);
338     m_inspectorPage->process().assumeReadAccessToBaseURL(WebInspectorProxy::inspectorBaseURL());
339 }
340
341 // Called by WebInspectorProxy messages
342 void WebInspectorProxy::createInspectorPage(IPC::Attachment connectionIdentifier, bool canAttach, bool underTest)
343 {
344     if (!m_inspectedPage)
345         return;
346
347     m_underTest = underTest;
348     createFrontendPage();
349
350     ASSERT(m_inspectorPage);
351     if (!m_inspectorPage)
352         return;
353
354     m_connectionIdentifier = WTFMove(connectionIdentifier);
355
356     m_inspectorPage->process().send(Messages::WebInspectorUI::EstablishConnection(m_connectionIdentifier, m_inspectedPage->pageID(), m_underTest, inspectionLevel()), m_inspectorPage->pageID());
357
358     if (!m_underTest) {
359         m_canAttach = platformCanAttach(canAttach);
360         m_isAttached = shouldOpenAttached();
361         m_attachmentSide = static_cast<AttachmentSide>(inspectorPagePreferences().inspectorAttachmentSide());
362
363         m_inspectedPage->process().send(Messages::WebInspector::SetAttached(m_isAttached), m_inspectedPage->pageID());
364
365         if (m_isAttached) {
366             switch (m_attachmentSide) {
367             case AttachmentSide::Bottom:
368                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedBottom(), m_inspectorPage->pageID());
369                 break;
370
371             case AttachmentSide::Right:
372                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedRight(), m_inspectorPage->pageID());
373                 break;
374
375             case AttachmentSide::Left:
376                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedLeft(), m_inspectorPage->pageID());
377                 break;
378             }
379         } else
380             m_inspectorPage->process().send(Messages::WebInspectorUI::Detached(), m_inspectorPage->pageID());
381
382         m_inspectorPage->process().send(Messages::WebInspectorUI::SetDockingUnavailable(!m_canAttach), m_inspectorPage->pageID());
383     }
384
385     m_inspectorPage->loadRequest(URL(URL(), m_underTest ? WebInspectorProxy::inspectorTestPageURL() : WebInspectorProxy::inspectorPageURL()));
386 }
387
388 void WebInspectorProxy::open()
389 {
390     if (m_underTest)
391         return;
392
393     if (!m_inspectorPage)
394         return;
395
396     m_isVisible = true;
397     m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
398
399     if (m_isAttached)
400         platformAttach();
401     else
402         platformCreateFrontendWindow();
403
404     platformBringToFront();
405 }
406
407 void WebInspectorProxy::didClose()
408 {
409     closeFrontendPageAndWindow();
410 }
411
412 void WebInspectorProxy::closeFrontendPageAndWindow()
413 {
414     if (!m_inspectorPage)
415         return;
416
417     m_isVisible = false;
418     m_isProfilingPage = false;
419     m_showMessageSent = false;
420     m_ignoreFirstBringToFront = false;
421
422     untrackInspectorPage(m_inspectorPage);
423
424     m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
425     m_inspectorPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
426
427     if (m_isAttached)
428         platformDetach();
429
430     // Null out m_inspectorPage after platformDetach(), so the views can be cleaned up correctly.
431     m_inspectorPage = nullptr;
432
433     m_isAttached = false;
434     m_canAttach = false;
435     m_underTest = false;
436
437     m_connectionIdentifier = IPC::Attachment();
438
439     platformCloseFrontendPageAndWindow();
440 }
441
442 void WebInspectorProxy::frontendLoaded()
443 {
444     if (auto* automationSession = m_inspectedPage->process().processPool().automationSession())
445         automationSession->inspectorFrontendLoaded(*m_inspectedPage);
446 }
447
448 void WebInspectorProxy::bringToFront()
449 {
450     // WebCore::InspectorFrontendClientLocal tells us to do this on load. We want to
451     // ignore it once if we only wanted to connect. This allows the Inspector to later
452     // request to be brought to the front when a breakpoint is hit or some other action.
453     if (m_ignoreFirstBringToFront) {
454         m_ignoreFirstBringToFront = false;
455         return;
456     }
457
458     if (m_isVisible)
459         platformBringToFront();
460     else
461         open();
462 }
463
464 void WebInspectorProxy::attachAvailabilityChanged(bool available)
465 {
466     bool previousCanAttach = m_canAttach;
467
468     m_canAttach = platformCanAttach(available);
469
470     if (previousCanAttach == m_canAttach)
471         return;
472
473     if (m_inspectorPage && !m_underTest)
474         m_inspectorPage->process().send(Messages::WebInspectorUI::SetDockingUnavailable(!m_canAttach), m_inspectorPage->pageID());
475
476     platformAttachAvailabilityChanged(m_canAttach);
477 }
478
479 void WebInspectorProxy::inspectedURLChanged(const String& urlString)
480 {
481     platformInspectedURLChanged(urlString);
482 }
483
484 void WebInspectorProxy::elementSelectionChanged(bool active)
485 {
486     m_elementSelectionActive = active;
487
488     if (m_ignoreElementSelectionChange) {
489         m_ignoreElementSelectionChange = false;
490         if (!m_isVisible)
491             close();
492         return;
493     }
494
495     if (active)
496         platformBringInspectedPageToFront();
497     else if (isConnected())
498         bringToFront();
499 }
500
501 void WebInspectorProxy::save(const String& filename, const String& content, bool base64Encoded, bool forceSaveAs)
502 {
503     platformSave(filename, content, base64Encoded, forceSaveAs);
504 }
505
506 void WebInspectorProxy::append(const String& filename, const String& content)
507 {
508     platformAppend(filename, content);
509 }
510
511 bool WebInspectorProxy::shouldOpenAttached()
512 {
513     return inspectorPagePreferences().inspectorStartsAttached() && canAttach();
514 }
515
516 // Unsupported configurations can use the stubs provided here.
517
518 #if PLATFORM(IOS) || (PLATFORM(MAC) && !WK_API_ENABLED)
519
520 WebPageProxy* WebInspectorProxy::platformCreateFrontendPage()
521 {
522     notImplemented();
523     return nullptr;
524 }
525
526 void WebInspectorProxy::platformCreateFrontendWindow()
527 {
528     notImplemented();
529 }
530
531 void WebInspectorProxy::platformCloseFrontendPageAndWindow()
532 {
533     notImplemented();
534 }
535
536 void WebInspectorProxy::platformDidCloseForCrash()
537 {
538     notImplemented();
539 }
540
541 void WebInspectorProxy::platformInvalidate()
542 {
543     notImplemented();
544 }
545
546 void WebInspectorProxy::platformBringToFront()
547 {
548     notImplemented();
549 }
550
551 void WebInspectorProxy::platformBringInspectedPageToFront()
552 {
553     notImplemented();
554 }
555
556 void WebInspectorProxy::platformHide()
557 {
558     notImplemented();
559 }
560
561 bool WebInspectorProxy::platformIsFront()
562 {
563     notImplemented();
564     return false;
565 }
566
567 void WebInspectorProxy::platformInspectedURLChanged(const String&)
568 {
569     notImplemented();
570 }
571
572 void WebInspectorProxy::platformSave(const String& suggestedURL, const String& content, bool base64Encoded, bool forceSaveDialog)
573 {
574     notImplemented();
575 }
576
577 void WebInspectorProxy::platformAppend(const String& suggestedURL, const String& content)
578 {
579     notImplemented();
580 }
581
582 unsigned WebInspectorProxy::platformInspectedWindowHeight()
583 {
584     notImplemented();
585     return 0;
586 }
587
588 unsigned WebInspectorProxy::platformInspectedWindowWidth()
589 {
590     notImplemented();
591     return 0;
592 }
593
594 void WebInspectorProxy::platformAttach()
595 {
596     notImplemented();
597 }
598
599 void WebInspectorProxy::platformDetach()
600 {
601     notImplemented();
602 }
603
604 void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned)
605 {
606     notImplemented();
607 }
608
609 void WebInspectorProxy::platformStartWindowDrag()
610 {
611     notImplemented();
612 }
613
614 String WebInspectorProxy::inspectorPageURL()
615 {
616     notImplemented();
617     return String();
618 }
619
620 String WebInspectorProxy::inspectorTestPageURL()
621 {
622     notImplemented();
623     return String();
624 }
625
626 String WebInspectorProxy::inspectorBaseURL()
627 {
628     notImplemented();
629     return String();
630 }
631
632 void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned)
633 {
634     notImplemented();
635 }
636
637 void WebInspectorProxy::platformAttachAvailabilityChanged(bool)
638 {
639     notImplemented();
640 }
641
642 #endif // PLATFORM(IOS) || (PLATFORM(MAC) && !WK_API_ENABLED)
643
644 } // namespace WebKit