Web Inspector: Keep Web Inspector window alive across process swaps (PSON) (Local...
[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 "APINavigation.h"
31 #include "APIProcessPoolConfiguration.h"
32 #include "WebAutomationSession.h"
33 #include "WebFrameProxy.h"
34 #include "WebInspectorInterruptDispatcherMessages.h"
35 #include "WebInspectorMessages.h"
36 #include "WebInspectorProxyMessages.h"
37 #include "WebInspectorUIMessages.h"
38 #include "WebPageGroup.h"
39 #include "WebPageInspectorController.h"
40 #include "WebPageProxy.h"
41 #include "WebPreferences.h"
42 #include "WebProcessPool.h"
43 #include "WebProcessProxy.h"
44 #include <WebCore/NotImplemented.h>
45 #include <WebCore/TextEncoding.h>
46 #include <wtf/SetForScope.h>
47
48 #if PLATFORM(GTK)
49 #include "WebInspectorProxyClient.h"
50 #endif
51
52 namespace WebKit {
53 using namespace WebCore;
54
55 const unsigned WebInspectorProxy::minimumWindowWidth = 500;
56 const unsigned WebInspectorProxy::minimumWindowHeight = 400;
57
58 const unsigned WebInspectorProxy::initialWindowWidth = 1000;
59 const unsigned WebInspectorProxy::initialWindowHeight = 650;
60
61 WebInspectorProxy::WebInspectorProxy(WebPageProxy* inspectedPage)
62     : m_inspectedPage(inspectedPage)
63 #if PLATFORM(MAC) && WK_API_ENABLED
64     , m_closeFrontendAfterInactivityTimer(RunLoop::main(), this, &WebInspectorProxy::closeFrontendAfterInactivityTimerFired)
65 #endif
66 {
67     m_inspectedPage->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID(), *this);
68 }
69
70 WebInspectorProxy::~WebInspectorProxy()
71 {
72 }
73
74 unsigned WebInspectorProxy::inspectionLevel() const
75 {
76     return inspectorLevelForPage(inspectedPage());
77 }
78
79 WebPreferences& WebInspectorProxy::inspectorPagePreferences() const
80 {
81     ASSERT(m_inspectorPage);
82     return m_inspectorPage->pageGroup().preferences();
83 }
84
85 void WebInspectorProxy::invalidate()
86 {
87     if (m_inspectedPage)
88         m_inspectedPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
89
90     closeFrontendPageAndWindow();
91     platformInvalidate();
92
93     m_inspectedPage = nullptr;
94 }
95
96 void WebInspectorProxy::sendMessageToFrontend(const String& message)
97 {
98     if (!m_inspectorPage)
99         return;
100
101     m_inspectorPage->process().send(Messages::WebInspectorUI::SendMessageToFrontend(message), m_inspectorPage->pageID());
102 }
103
104 // Public APIs
105 bool WebInspectorProxy::isFront()
106 {
107     if (!m_inspectedPage)
108         return false;
109
110     return platformIsFront();
111 }
112
113 void WebInspectorProxy::connect()
114 {
115     if (!m_inspectedPage)
116         return;
117
118     if (m_showMessageSent)
119         return;
120
121     m_showMessageSent = true;
122     m_ignoreFirstBringToFront = true;
123
124     createFrontendPage();
125
126     m_inspectedPage->process().send(Messages::WebInspectorInterruptDispatcher::NotifyNeedDebuggerBreak(), 0);
127     m_inspectedPage->process().send(Messages::WebInspector::Show(), m_inspectedPage->pageID());
128 }
129
130 void WebInspectorProxy::show()
131 {
132     if (!m_inspectedPage)
133         return;
134
135     if (isConnected()) {
136         bringToFront();
137         return;
138     }
139
140     connect();
141
142     // Don't ignore the first bringToFront so it opens the Inspector.
143     m_ignoreFirstBringToFront = false;
144 }
145
146 void WebInspectorProxy::hide()
147 {
148     if (!m_inspectedPage)
149         return;
150
151     m_isVisible = false;
152
153     platformHide();
154 }
155
156 void WebInspectorProxy::close()
157 {
158     if (!m_inspectedPage)
159         return;
160
161     m_inspectedPage->process().send(Messages::WebInspector::Close(), m_inspectedPage->pageID());
162
163     closeFrontendPageAndWindow();
164 }
165
166 void WebInspectorProxy::closeForCrash()
167 {
168     close();
169
170     platformDidCloseForCrash();
171 }
172
173 void WebInspectorProxy::reset()
174 {
175     if (m_inspectedPage) {
176         m_inspectedPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
177         m_inspectedPage = nullptr;
178     }
179 }
180
181 void WebInspectorProxy::updateForNewPageProcess(WebPageProxy* inspectedPage)
182 {
183     ASSERT(!m_inspectedPage);
184     ASSERT(inspectedPage);
185
186     m_inspectedPage = inspectedPage;
187     m_inspectedPage->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID(), *this);
188
189     if (m_inspectorPage)
190         m_inspectorPage->process().send(Messages::WebInspectorUI::UpdateConnection(), m_inspectorPage->pageID());
191 }
192
193 void WebInspectorProxy::setFrontendConnection(IPC::Attachment connectionIdentifier)
194 {
195     if (!m_inspectedPage)
196         return;
197
198     m_inspectedPage->process().send(Messages::WebInspector::SetFrontendConnection(connectionIdentifier), m_inspectedPage->pageID());
199 }
200
201 void WebInspectorProxy::showConsole()
202 {
203     if (!m_inspectedPage)
204         return;
205
206     createFrontendPage();
207
208     m_inspectedPage->process().send(Messages::WebInspector::ShowConsole(), m_inspectedPage->pageID());
209 }
210
211 void WebInspectorProxy::showResources()
212 {
213     if (!m_inspectedPage)
214         return;
215
216     createFrontendPage();
217
218     m_inspectedPage->process().send(Messages::WebInspector::ShowResources(), m_inspectedPage->pageID());
219 }
220
221 void WebInspectorProxy::showTimelines()
222 {
223     if (!m_inspectedPage)
224         return;
225
226     createFrontendPage();
227
228     m_inspectedPage->process().send(Messages::WebInspector::ShowTimelines(), m_inspectedPage->pageID());
229 }
230
231 void WebInspectorProxy::showMainResourceForFrame(WebFrameProxy* frame)
232 {
233     if (!m_inspectedPage)
234         return;
235
236     createFrontendPage();
237
238     m_inspectedPage->process().send(Messages::WebInspector::ShowMainResourceForFrame(frame->frameID()), m_inspectedPage->pageID());
239 }
240
241 void WebInspectorProxy::attachBottom()
242 {
243     attach(AttachmentSide::Bottom);
244 }
245
246 void WebInspectorProxy::attachRight()
247 {
248     attach(AttachmentSide::Right);
249 }
250
251 void WebInspectorProxy::attachLeft()
252 {
253     attach(AttachmentSide::Left);
254 }
255
256 void WebInspectorProxy::attach(AttachmentSide side)
257 {
258     if (!m_inspectedPage || !canAttach())
259         return;
260
261     m_isAttached = true;
262     m_attachmentSide = side;
263
264     inspectorPagePreferences().setInspectorAttachmentSide(static_cast<uint32_t>(side));
265
266     if (m_isVisible)
267         inspectorPagePreferences().setInspectorStartsAttached(true);
268
269     m_inspectedPage->process().send(Messages::WebInspector::SetAttached(true), m_inspectedPage->pageID());
270
271     switch (m_attachmentSide) {
272     case AttachmentSide::Bottom:
273         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedBottom(), m_inspectorPage->pageID());
274         break;
275
276     case AttachmentSide::Right:
277         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedRight(), m_inspectorPage->pageID());
278         break;
279
280     case AttachmentSide::Left:
281         m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedLeft(), m_inspectorPage->pageID());
282         break;
283     }
284
285     platformAttach();
286 }
287
288 void WebInspectorProxy::detach()
289 {
290     if (!m_inspectedPage)
291         return;
292
293     m_isAttached = false;
294
295     if (m_isVisible)
296         inspectorPagePreferences().setInspectorStartsAttached(false);
297
298     m_inspectedPage->process().send(Messages::WebInspector::SetAttached(false), m_inspectedPage->pageID());
299     m_inspectorPage->process().send(Messages::WebInspectorUI::Detached(), m_inspectorPage->pageID());
300
301     platformDetach();
302 }
303
304 void WebInspectorProxy::setAttachedWindowHeight(unsigned height)
305 {
306     inspectorPagePreferences().setInspectorAttachedHeight(height);
307     platformSetAttachedWindowHeight(height);
308 }
309
310 void WebInspectorProxy::setAttachedWindowWidth(unsigned width)
311 {
312     inspectorPagePreferences().setInspectorAttachedWidth(width);
313     platformSetAttachedWindowWidth(width);
314 }
315
316 void WebInspectorProxy::startWindowDrag()
317 {
318     platformStartWindowDrag();
319 }
320
321 void WebInspectorProxy::togglePageProfiling()
322 {
323     if (!m_inspectedPage)
324         return;
325
326     if (m_isProfilingPage)
327         m_inspectedPage->process().send(Messages::WebInspector::StopPageProfiling(), m_inspectedPage->pageID());
328     else
329         m_inspectedPage->process().send(Messages::WebInspector::StartPageProfiling(), m_inspectedPage->pageID());
330
331     // FIXME: have the WebProcess notify us on state changes.
332     m_isProfilingPage = !m_isProfilingPage;
333 }
334
335 void WebInspectorProxy::toggleElementSelection()
336 {
337     if (!m_inspectedPage)
338         return;
339
340     if (m_elementSelectionActive) {
341         m_ignoreElementSelectionChange = true;
342         m_inspectedPage->process().send(Messages::WebInspector::StopElementSelection(), m_inspectedPage->pageID());
343     } else {
344         connect();
345         m_inspectedPage->process().send(Messages::WebInspector::StartElementSelection(), m_inspectedPage->pageID());
346     }
347 }
348
349 bool WebInspectorProxy::isMainOrTestInspectorPage(const URL& url)
350 {
351     // Use URL so we can compare the paths and protocols.
352     URL mainPageURL(URL(), WebInspectorProxy::inspectorPageURL());
353     if (url.protocol() == mainPageURL.protocol() && decodeURLEscapeSequences(url.path()) == decodeURLEscapeSequences(mainPageURL.path()))
354         return true;
355
356     // We might not have a Test URL in Production builds.
357     String testPageURLString = WebInspectorProxy::inspectorTestPageURL();
358     if (testPageURLString.isNull())
359         return false;
360
361     URL testPageURL(URL(), testPageURLString);
362     return url.protocol() == testPageURL.protocol() && decodeURLEscapeSequences(url.path()) == decodeURLEscapeSequences(testPageURL.path());
363 }
364
365 void WebInspectorProxy::createFrontendPage()
366 {
367     if (m_inspectorPage)
368         return;
369
370     m_inspectorPage = platformCreateFrontendPage();
371     ASSERT(m_inspectorPage);
372     if (!m_inspectorPage)
373         return;
374
375     trackInspectorPage(m_inspectorPage);
376
377     m_inspectorPage->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID(), *this);
378     m_inspectorPage->process().assumeReadAccessToBaseURL(WebInspectorProxy::inspectorBaseURL());
379 }
380
381 void WebInspectorProxy::openLocalInspectorFrontend(bool canAttach, bool underTest)
382 {
383     if (!m_inspectedPage)
384         return;
385
386     if (m_inspectedPage->inspectorController().hasLocalFrontend()) {
387         show();
388         return;
389     }
390
391     m_underTest = underTest;
392     createFrontendPage();
393
394     ASSERT(m_inspectorPage);
395     if (!m_inspectorPage)
396         return;
397
398     m_inspectorPage->process().send(Messages::WebInspectorUI::EstablishConnection(m_inspectedPage->pageID(), m_underTest, inspectionLevel()), m_inspectorPage->pageID());
399
400     m_inspectedPage->inspectorController().connectFrontend(*this);
401
402     if (!m_underTest) {
403         m_canAttach = platformCanAttach(canAttach);
404         m_isAttached = shouldOpenAttached();
405         m_attachmentSide = static_cast<AttachmentSide>(inspectorPagePreferences().inspectorAttachmentSide());
406
407         m_inspectedPage->process().send(Messages::WebInspector::SetAttached(m_isAttached), m_inspectedPage->pageID());
408
409         if (m_isAttached) {
410             switch (m_attachmentSide) {
411             case AttachmentSide::Bottom:
412                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedBottom(), m_inspectorPage->pageID());
413                 break;
414
415             case AttachmentSide::Right:
416                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedRight(), m_inspectorPage->pageID());
417                 break;
418
419             case AttachmentSide::Left:
420                 m_inspectorPage->process().send(Messages::WebInspectorUI::AttachedLeft(), m_inspectorPage->pageID());
421                 break;
422             }
423         } else
424             m_inspectorPage->process().send(Messages::WebInspectorUI::Detached(), m_inspectorPage->pageID());
425
426         m_inspectorPage->process().send(Messages::WebInspectorUI::SetDockingUnavailable(!m_canAttach), m_inspectorPage->pageID());
427     }
428
429     m_inspectorPage->loadRequest(URL(URL(), m_underTest ? WebInspectorProxy::inspectorTestPageURL() : WebInspectorProxy::inspectorPageURL()));
430 }
431
432 void WebInspectorProxy::open()
433 {
434     if (m_underTest)
435         return;
436
437     if (!m_inspectorPage)
438         return;
439
440     SetForScope<bool> isOpening(m_isOpening, true);
441     m_isVisible = true;
442     m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
443
444     if (m_isAttached)
445         platformAttach();
446     else
447         platformCreateFrontendWindow();
448
449     platformBringToFront();
450 }
451
452 void WebInspectorProxy::didClose()
453 {
454     closeFrontendPageAndWindow();
455 }
456
457 void WebInspectorProxy::closeFrontendPageAndWindow()
458 {
459     if (!m_inspectorPage)
460         return;
461
462     m_isVisible = false;
463     m_isProfilingPage = false;
464     m_showMessageSent = false;
465     m_ignoreFirstBringToFront = false;
466
467     untrackInspectorPage(m_inspectorPage);
468
469     m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
470     m_inspectorPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
471
472     m_inspectedPage->inspectorController().disconnectFrontend(*this);
473
474     if (m_isAttached)
475         platformDetach();
476
477     // Null out m_inspectorPage after platformDetach(), so the views can be cleaned up correctly.
478     m_inspectorPage = nullptr;
479
480     m_isAttached = false;
481     m_canAttach = false;
482     m_underTest = false;
483
484     platformCloseFrontendPageAndWindow();
485 }
486
487 void WebInspectorProxy::sendMessageToBackend(const String& message)
488 {
489     if (!m_inspectedPage)
490         return;
491
492     m_inspectedPage->inspectorController().dispatchMessageFromFrontend(message);
493 }
494
495 void WebInspectorProxy::frontendLoaded()
496 {
497     if (!m_inspectedPage)
498         return;
499
500     if (auto* automationSession = m_inspectedPage->process().processPool().automationSession())
501         automationSession->inspectorFrontendLoaded(*m_inspectedPage);
502 }
503
504 void WebInspectorProxy::bringToFront()
505 {
506     // WebCore::InspectorFrontendClientLocal tells us to do this on load. We want to
507     // ignore it once if we only wanted to connect. This allows the Inspector to later
508     // request to be brought to the front when a breakpoint is hit or some other action.
509     if (m_ignoreFirstBringToFront) {
510         m_ignoreFirstBringToFront = false;
511         return;
512     }
513
514     if (m_isVisible)
515         platformBringToFront();
516     else
517         open();
518 }
519
520 void WebInspectorProxy::attachAvailabilityChanged(bool available)
521 {
522     bool previousCanAttach = m_canAttach;
523
524     m_canAttach = platformCanAttach(available);
525
526     if (previousCanAttach == m_canAttach)
527         return;
528
529     if (m_inspectorPage && !m_underTest)
530         m_inspectorPage->process().send(Messages::WebInspectorUI::SetDockingUnavailable(!m_canAttach), m_inspectorPage->pageID());
531
532     platformAttachAvailabilityChanged(m_canAttach);
533 }
534
535 void WebInspectorProxy::inspectedURLChanged(const String& urlString)
536 {
537     platformInspectedURLChanged(urlString);
538 }
539
540 void WebInspectorProxy::elementSelectionChanged(bool active)
541 {
542     m_elementSelectionActive = active;
543
544     if (m_ignoreElementSelectionChange) {
545         m_ignoreElementSelectionChange = false;
546         if (!m_isVisible)
547             close();
548         return;
549     }
550
551     if (active)
552         platformBringInspectedPageToFront();
553     else if (isConnected())
554         bringToFront();
555 }
556
557 void WebInspectorProxy::save(const String& filename, const String& content, bool base64Encoded, bool forceSaveAs)
558 {
559     platformSave(filename, content, base64Encoded, forceSaveAs);
560 }
561
562 void WebInspectorProxy::append(const String& filename, const String& content)
563 {
564     platformAppend(filename, content);
565 }
566
567 bool WebInspectorProxy::shouldOpenAttached()
568 {
569     return inspectorPagePreferences().inspectorStartsAttached() && canAttach();
570 }
571
572 // Unsupported configurations can use the stubs provided here.
573
574 #if PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && !WK_API_ENABLED)
575
576 WebPageProxy* WebInspectorProxy::platformCreateFrontendPage()
577 {
578     notImplemented();
579     return nullptr;
580 }
581
582 void WebInspectorProxy::platformCreateFrontendWindow()
583 {
584     notImplemented();
585 }
586
587 void WebInspectorProxy::platformCloseFrontendPageAndWindow()
588 {
589     notImplemented();
590 }
591
592 void WebInspectorProxy::platformDidCloseForCrash()
593 {
594     notImplemented();
595 }
596
597 void WebInspectorProxy::platformInvalidate()
598 {
599     notImplemented();
600 }
601
602 void WebInspectorProxy::platformBringToFront()
603 {
604     notImplemented();
605 }
606
607 void WebInspectorProxy::platformBringInspectedPageToFront()
608 {
609     notImplemented();
610 }
611
612 void WebInspectorProxy::platformHide()
613 {
614     notImplemented();
615 }
616
617 bool WebInspectorProxy::platformIsFront()
618 {
619     notImplemented();
620     return false;
621 }
622
623 void WebInspectorProxy::platformInspectedURLChanged(const String&)
624 {
625     notImplemented();
626 }
627
628 void WebInspectorProxy::platformSave(const String& suggestedURL, const String& content, bool base64Encoded, bool forceSaveDialog)
629 {
630     notImplemented();
631 }
632
633 void WebInspectorProxy::platformAppend(const String& suggestedURL, const String& content)
634 {
635     notImplemented();
636 }
637
638 unsigned WebInspectorProxy::platformInspectedWindowHeight()
639 {
640     notImplemented();
641     return 0;
642 }
643
644 unsigned WebInspectorProxy::platformInspectedWindowWidth()
645 {
646     notImplemented();
647     return 0;
648 }
649
650 void WebInspectorProxy::platformAttach()
651 {
652     notImplemented();
653 }
654
655 void WebInspectorProxy::platformDetach()
656 {
657     notImplemented();
658 }
659
660 void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned)
661 {
662     notImplemented();
663 }
664
665 void WebInspectorProxy::platformStartWindowDrag()
666 {
667     notImplemented();
668 }
669
670 String WebInspectorProxy::inspectorPageURL()
671 {
672     notImplemented();
673     return String();
674 }
675
676 String WebInspectorProxy::inspectorTestPageURL()
677 {
678     notImplemented();
679     return String();
680 }
681
682 String WebInspectorProxy::inspectorBaseURL()
683 {
684     notImplemented();
685     return String();
686 }
687
688 void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned)
689 {
690     notImplemented();
691 }
692
693 void WebInspectorProxy::platformAttachAvailabilityChanged(bool)
694 {
695     notImplemented();
696 }
697
698 #endif // PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && !WK_API_ENABLED)
699
700 } // namespace WebKit