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