2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Portions Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "WebInspectorProxy.h"
32 #include "APIURLRequest.h"
33 #include "WebFramePolicyListenerProxy.h"
34 #include "WebFrameProxy.h"
35 #include "WebInspectorMessages.h"
36 #include "WebInspectorProxyMessages.h"
37 #include "WebPageCreationParameters.h"
38 #include "WebPageGroup.h"
39 #include "WebPageProxy.h"
40 #include "WebPreferences.h"
41 #include "WebProcessProxy.h"
42 #include <WebCore/SchemeRegistry.h>
43 #include <wtf/NeverDestroyed.h>
44 #include <wtf/text/StringBuilder.h>
46 #if ENABLE(INSPECTOR_SERVER)
47 #include "WebInspectorServer.h"
50 using namespace WebCore;
54 const unsigned WebInspectorProxy::minimumWindowWidth = 750;
55 const unsigned WebInspectorProxy::minimumWindowHeight = 400;
57 const unsigned WebInspectorProxy::initialWindowWidth = 1000;
58 const unsigned WebInspectorProxy::initialWindowHeight = 650;
60 const unsigned WebInspectorProxy::minimumAttachedWidth = 750;
61 const unsigned WebInspectorProxy::minimumAttachedHeight = 250;
63 class WebInspectorPageGroups {
65 static WebInspectorPageGroups& shared()
67 static NeverDestroyed<WebInspectorPageGroups> instance;
71 unsigned inspectorLevel(WebPageGroup& inspectedPageGroup)
73 return isInspectorPageGroup(inspectedPageGroup) ? inspectorPageGroupLevel(inspectedPageGroup) + 1 : 1;
76 bool isInspectorPageGroup(WebPageGroup& group)
78 return m_pageGroupLevel.contains(&group);
81 unsigned inspectorPageGroupLevel(WebPageGroup& group)
83 ASSERT(isInspectorPageGroup(group));
84 return m_pageGroupLevel.get(&group);
87 WebPageGroup* inspectorPageGroupForLevel(unsigned level)
89 // The level is the key of the HashMap, so it cannot be 0.
92 auto iterator = m_pageGroupByLevel.find(level);
93 if (iterator != m_pageGroupByLevel.end())
94 return iterator->value.get();
96 RefPtr<WebPageGroup> group = createInspectorPageGroup(level);
97 m_pageGroupByLevel.set(level, group.get());
98 m_pageGroupLevel.set(group.get(), level);
103 static PassRefPtr<WebPageGroup> createInspectorPageGroup(unsigned level)
105 RefPtr<WebPageGroup> pageGroup = WebPageGroup::create(String::format("__WebInspectorPageGroupLevel%u__", level), false, false);
108 // Allow developers to inspect the Web Inspector in debug builds.
109 pageGroup->preferences().setDeveloperExtrasEnabled(true);
110 pageGroup->preferences().setLogsPageMessagesToSystemConsoleEnabled(true);
113 pageGroup->preferences().setApplicationChromeModeEnabled(true);
115 return pageGroup.release();
118 typedef HashMap<unsigned, RefPtr<WebPageGroup> > PageGroupByLevelMap;
119 typedef HashMap<WebPageGroup*, unsigned> PageGroupLevelMap;
121 PageGroupByLevelMap m_pageGroupByLevel;
122 PageGroupLevelMap m_pageGroupLevel;
125 WebInspectorProxy::WebInspectorProxy(WebPageProxy* page)
128 , m_isAttached(false)
129 , m_isDebuggingJavaScript(false)
130 , m_isProfilingJavaScript(false)
131 , m_isProfilingPage(false)
132 , m_showMessageSent(false)
133 , m_createdInspectorPage(false)
134 , m_ignoreFirstBringToFront(false)
135 , m_attachmentSide(AttachmentSideBottom)
136 #if PLATFORM(GTK) || PLATFORM(EFL)
138 , m_inspectorWindow(0)
140 #if ENABLE(INSPECTOR_SERVER)
141 , m_remoteInspectionPageId(0)
144 m_level = WebInspectorPageGroups::shared().inspectorLevel(m_page->pageGroup());
145 m_page->process().addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_page->pageID(), *this);
148 WebInspectorProxy::~WebInspectorProxy()
152 WebPageGroup* WebInspectorProxy::inspectorPageGroup() const
154 return WebInspectorPageGroups::shared().inspectorPageGroupForLevel(m_level);
157 void WebInspectorProxy::invalidate()
159 #if ENABLE(INSPECTOR_SERVER)
160 if (m_remoteInspectionPageId)
161 WebInspectorServer::shared().unregisterPage(m_remoteInspectionPageId);
164 m_page->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_page->pageID());
172 bool WebInspectorProxy::isFront()
177 return platformIsFront();
180 void WebInspectorProxy::connect()
185 if (m_showMessageSent)
188 m_showMessageSent = true;
189 m_ignoreFirstBringToFront = true;
191 m_page->process().send(Messages::WebInspector::Show(), m_page->pageID());
194 void WebInspectorProxy::show()
206 // Don't ignore the first bringToFront so it opens the Inspector.
207 m_ignoreFirstBringToFront = false;
210 void WebInspectorProxy::hide()
220 void WebInspectorProxy::close()
225 m_page->process().send(Messages::WebInspector::Close(), m_page->pageID());
230 void WebInspectorProxy::showConsole()
235 m_page->process().send(Messages::WebInspector::ShowConsole(), m_page->pageID());
238 void WebInspectorProxy::showResources()
243 m_page->process().send(Messages::WebInspector::ShowResources(), m_page->pageID());
246 void WebInspectorProxy::showMainResourceForFrame(WebFrameProxy* frame)
251 m_page->process().send(Messages::WebInspector::ShowMainResourceForFrame(frame->frameID()), m_page->pageID());
254 void WebInspectorProxy::attachBottom()
256 attach(AttachmentSideBottom);
259 void WebInspectorProxy::attachRight()
261 attach(AttachmentSideRight);
264 void WebInspectorProxy::attach(AttachmentSide side)
266 if (!m_page || !canAttach())
270 m_attachmentSide = side;
272 inspectorPageGroup()->preferences().setInspectorAttachmentSide(side);
275 inspectorPageGroup()->preferences().setInspectorStartsAttached(true);
277 switch (m_attachmentSide) {
278 case AttachmentSideBottom:
279 m_page->process().send(Messages::WebInspector::AttachedBottom(), m_page->pageID());
282 case AttachmentSideRight:
283 m_page->process().send(Messages::WebInspector::AttachedRight(), m_page->pageID());
290 void WebInspectorProxy::detach()
295 m_isAttached = false;
298 inspectorPageGroup()->preferences().setInspectorStartsAttached(false);
300 m_page->process().send(Messages::WebInspector::Detached(), m_page->pageID());
305 void WebInspectorProxy::setAttachedWindowHeight(unsigned height)
307 inspectorPageGroup()->preferences().setInspectorAttachedHeight(height);
308 platformSetAttachedWindowHeight(height);
311 void WebInspectorProxy::setAttachedWindowWidth(unsigned width)
313 inspectorPageGroup()->preferences().setInspectorAttachedWidth(width);
314 platformSetAttachedWindowWidth(width);
317 void WebInspectorProxy::toggleJavaScriptDebugging()
322 if (m_isDebuggingJavaScript)
323 m_page->process().send(Messages::WebInspector::StopJavaScriptDebugging(), m_page->pageID());
325 m_page->process().send(Messages::WebInspector::StartJavaScriptDebugging(), m_page->pageID());
327 // FIXME: have the WebProcess notify us on state changes.
328 m_isDebuggingJavaScript = !m_isDebuggingJavaScript;
331 void WebInspectorProxy::toggleJavaScriptProfiling()
336 if (m_isProfilingJavaScript)
337 m_page->process().send(Messages::WebInspector::StopJavaScriptProfiling(), m_page->pageID());
339 m_page->process().send(Messages::WebInspector::StartJavaScriptProfiling(), m_page->pageID());
341 // FIXME: have the WebProcess notify us on state changes.
342 m_isProfilingJavaScript = !m_isProfilingJavaScript;
345 void WebInspectorProxy::togglePageProfiling()
350 if (m_isProfilingPage)
351 m_page->process().send(Messages::WebInspector::StopPageProfiling(), m_page->pageID());
353 m_page->process().send(Messages::WebInspector::StartPageProfiling(), m_page->pageID());
355 // FIXME: have the WebProcess notify us on state changes.
356 m_isProfilingPage = !m_isProfilingPage;
359 bool WebInspectorProxy::isInspectorPage(WebPageProxy& page)
361 return WebInspectorPageGroups::shared().isInspectorPageGroup(page.pageGroup());
364 static bool isMainOrTestInspectorPage(const WebInspectorProxy* webInspectorProxy, WKURLRequestRef requestRef)
366 URL requestURL(URL(), toImpl(requestRef)->resourceRequest().url());
367 if (!WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(requestURL.protocol()))
370 // Use URL so we can compare just the paths.
371 URL mainPageURL(URL(), webInspectorProxy->inspectorPageURL());
372 ASSERT(WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(mainPageURL.protocol()));
373 if (decodeURLEscapeSequences(requestURL.path()) == decodeURLEscapeSequences(mainPageURL.path()))
376 // We might not have a Test URL in Production builds.
377 String testPageURLString = webInspectorProxy->inspectorTestPageURL();
378 if (testPageURLString.isNull())
381 URL testPageURL(URL(), webInspectorProxy->inspectorTestPageURL());
382 ASSERT(WebCore::SchemeRegistry::shouldTreatURLSchemeAsLocal(testPageURL.protocol()));
383 return decodeURLEscapeSequences(requestURL.path()) == decodeURLEscapeSequences(testPageURL.path());
386 static void decidePolicyForNavigationAction(WKPageRef, WKFrameRef frameRef, WKFrameNavigationType, WKEventModifiers, WKEventMouseButton, WKFrameRef, WKURLRequestRef requestRef, WKFramePolicyListenerRef listenerRef, WKTypeRef, const void* clientInfo)
388 // Allow non-main frames to navigate anywhere.
389 if (!toImpl(frameRef)->isMainFrame()) {
390 toImpl(listenerRef)->use();
394 const WebInspectorProxy* webInspectorProxy = static_cast<const WebInspectorProxy*>(clientInfo);
395 ASSERT(webInspectorProxy);
397 // Allow loading of the main inspector file.
398 if (isMainOrTestInspectorPage(webInspectorProxy, requestRef)) {
399 toImpl(listenerRef)->use();
403 // Prevent everything else from loading in the inspector's page.
404 toImpl(listenerRef)->ignore();
406 // And instead load it in the inspected page.
407 webInspectorProxy->page()->loadRequest(toImpl(requestRef)->resourceRequest());
410 #if ENABLE(INSPECTOR_SERVER)
411 void WebInspectorProxy::enableRemoteInspection()
413 if (!m_remoteInspectionPageId)
414 m_remoteInspectionPageId = WebInspectorServer::shared().registerPage(this);
417 void WebInspectorProxy::remoteFrontendConnected()
419 m_page->process().send(Messages::WebInspector::RemoteFrontendConnected(), m_page->pageID());
422 void WebInspectorProxy::remoteFrontendDisconnected()
424 m_page->process().send(Messages::WebInspector::RemoteFrontendDisconnected(), m_page->pageID());
427 void WebInspectorProxy::dispatchMessageFromRemoteFrontend(const String& message)
429 m_page->process().send(Messages::WebInspector::DispatchMessageFromRemoteFrontend(message), m_page->pageID());
433 // Called by WebInspectorProxy messages
434 void WebInspectorProxy::createInspectorPage(uint64_t& inspectorPageID, WebPageCreationParameters& inspectorPageParameters)
441 m_isAttached = shouldOpenAttached();
442 m_attachmentSide = static_cast<AttachmentSide>(inspectorPageGroup()->preferences().inspectorAttachmentSide());
444 WebPageProxy* inspectorPage = platformCreateInspectorPage();
445 ASSERT(inspectorPage);
449 inspectorPageID = inspectorPage->pageID();
450 inspectorPageParameters = inspectorPage->creationParameters();
452 WKPagePolicyClientV1 policyClient = {
454 0, /* decidePolicyForNavigationAction_deprecatedForUseWithV0 */
455 0, /* decidePolicyForNewWindowAction */
456 0, /* decidePolicyForResponse_deprecatedForUseWithV0 */
457 0, /* unableToImplementPolicy */
458 decidePolicyForNavigationAction,
459 0, /* decidePolicyForResponse */
462 WKPageSetPagePolicyClient(toAPI(inspectorPage), &policyClient.base);
466 url.append(inspectorPageURL());
468 url.appendLiteral("?dockSide=");
471 switch (m_attachmentSide) {
472 case AttachmentSideBottom:
473 url.appendLiteral("bottom");
474 m_page->process().send(Messages::WebInspector::AttachedBottom(), m_page->pageID());
476 case AttachmentSideRight:
477 url.appendLiteral("right");
478 m_page->process().send(Messages::WebInspector::AttachedRight(), m_page->pageID());
482 url.appendLiteral("undocked");
484 m_page->process().assumeReadAccessToBaseURL(inspectorBaseURL());
486 inspectorPage->loadRequest(URL(URL(), url.toString()));
488 m_createdInspectorPage = true;
491 void WebInspectorProxy::createInspectorPageForTest(uint64_t& inspectorPageID, WebPageCreationParameters& inspectorPageParameters)
498 m_isAttached = false;
500 WebPageProxy* inspectorPage = platformCreateInspectorPage();
501 ASSERT(inspectorPage);
505 inspectorPageID = inspectorPage->pageID();
506 inspectorPageParameters = inspectorPage->creationParameters();
508 WKPagePolicyClientV1 policyClient = {
510 0, /* decidePolicyForNavigationAction_deprecatedForUseWithV0 */
511 0, /* decidePolicyForNewWindowAction */
512 0, /* decidePolicyForResponse_deprecatedForUseWithV0 */
513 0, /* unableToImplementPolicy */
514 decidePolicyForNavigationAction,
515 0, /* decidePolicyForResponse */
518 WKPageSetPagePolicyClient(toAPI(inspectorPage), &policyClient.base);
520 m_page->process().assumeReadAccessToBaseURL(inspectorBaseURL());
522 inspectorPage->loadRequest(URL(URL(), inspectorTestPageURL()));
524 m_createdInspectorPage = true;
527 void WebInspectorProxy::open()
534 void WebInspectorProxy::didClose()
536 if (!m_createdInspectorPage)
540 m_isDebuggingJavaScript = false;
541 m_isProfilingJavaScript = false;
542 m_isProfilingPage = false;
543 m_createdInspectorPage = false;
544 m_showMessageSent = false;
545 m_ignoreFirstBringToFront = false;
549 m_isAttached = false;
554 void WebInspectorProxy::bringToFront()
556 // WebCore::InspectorFrontendClientLocal tells us to do this on load. We want to
557 // ignore it once if we only wanted to connect. This allows the Inspector to later
558 // request to be brought to the front when a breakpoint is hit or some other action.
559 if (m_ignoreFirstBringToFront) {
560 m_ignoreFirstBringToFront = false;
565 platformBringToFront();
570 void WebInspectorProxy::attachAvailabilityChanged(bool available)
572 platformAttachAvailabilityChanged(available);
575 void WebInspectorProxy::inspectedURLChanged(const String& urlString)
577 platformInspectedURLChanged(urlString);
580 void WebInspectorProxy::save(const String& filename, const String& content, bool base64Encoded, bool forceSaveAs)
582 platformSave(filename, content, base64Encoded, forceSaveAs);
585 void WebInspectorProxy::append(const String& filename, const String& content)
587 platformAppend(filename, content);
590 bool WebInspectorProxy::canAttach()
592 // Keep this in sync with InspectorFrontendClientLocal::canAttachWindow. There are two implementations
593 // to make life easier in the multi-process world we have. WebInspectorProxy uses canAttach to decide if
594 // we can attach on open (on the UI process side). And InspectorFrontendClientLocal::canAttachWindow is
595 // used to decide if we can attach when the attach button is pressed (on the WebProcess side).
597 // If we are already attached, allow attaching again to allow switching sides.
601 // Don't allow attaching to another inspector -- two inspectors in one window is too much!
605 // Don't allow the attach if the window would be too small to accommodate the minimum inspector height.
606 unsigned inspectedPageHeight = platformInspectedWindowHeight();
607 unsigned inspectedPageWidth = platformInspectedWindowWidth();
608 unsigned maximumAttachedHeight = inspectedPageHeight * 3 / 4;
609 return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
612 bool WebInspectorProxy::shouldOpenAttached()
614 return inspectorPageGroup()->preferences().inspectorStartsAttached() && canAttach();
617 #if ENABLE(INSPECTOR_SERVER)
618 void WebInspectorProxy::sendMessageToRemoteFrontend(const String& message)
620 ASSERT(m_remoteInspectionPageId);
621 WebInspectorServer::shared().sendMessageOverConnection(m_remoteInspectionPageId, message);
625 } // namespace WebKit
627 #endif // ENABLE(INSPECTOR)