2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "InspectorFrontendClientLocal.h"
38 #include "DOMWrapperWorld.h"
40 #include "FloatRect.h"
42 #include "FrameLoadRequest.h"
43 #include "FrameLoader.h"
44 #include "FrameView.h"
45 #include "InspectorBackendDispatcher.h"
46 #include "InspectorController.h"
47 #include "InspectorFrontendHost.h"
48 #include "InspectorPageAgent.h"
50 #include "ScriptController.h"
51 #include "ScriptFunctionCall.h"
52 #include "ScriptObject.h"
53 #include "ScriptState.h"
56 #include "UserGestureIndicator.h"
57 #include "WindowFeatures.h"
58 #include <wtf/Deque.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/WTFString.h>
64 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
65 static const unsigned defaultAttachedHeight = 300;
66 static const float minimumAttachedHeight = 250.0f;
67 static const float maximumAttachedHeightRatio = 0.75f;
68 static const float minimumAttachedWidth = 750.0f;
69 static const float minimumAttachedInspectedWidth = 320.0f;
71 class InspectorBackendDispatchTask {
72 WTF_MAKE_FAST_ALLOCATED;
74 InspectorBackendDispatchTask(InspectorController* inspectorController)
75 : m_inspectorController(inspectorController)
76 , m_timer(this, &InspectorBackendDispatchTask::onTimer)
80 void dispatch(const String& message)
82 m_messages.append(message);
83 if (!m_timer.isActive())
84 m_timer.startOneShot(0);
93 void onTimer(Timer<InspectorBackendDispatchTask>*)
95 if (!m_messages.isEmpty()) {
96 // Dispatch can lead to the timer destruction -> schedule the next shot first.
97 m_timer.startOneShot(0);
98 m_inspectorController->dispatchMessageFromFrontend(m_messages.takeFirst());
103 InspectorController* m_inspectorController;
104 Timer<InspectorBackendDispatchTask> m_timer;
105 Deque<String> m_messages;
108 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
113 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
117 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage, PassOwnPtr<Settings> settings)
118 : m_inspectorController(inspectorController)
119 , m_frontendPage(frontendPage)
120 , m_settings(settings)
121 , m_frontendLoaded(false)
122 , m_dockSide(UNDOCKED)
124 m_frontendPage->settings()->setAllowFileAccessFromFileURLs(true);
125 m_dispatchTask = adoptPtr(new InspectorBackendDispatchTask(inspectorController));
128 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
131 m_frontendHost->disconnectClient();
133 m_inspectorController = 0;
136 void InspectorFrontendClientLocal::windowObjectCleared()
139 m_frontendHost->disconnectClient();
141 ScriptState* frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
142 m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
143 ScriptGlobalObject::set(frontendScriptState, "InspectorFrontendHost", m_frontendHost.get());
146 void InspectorFrontendClientLocal::frontendLoaded()
148 // Call setDockingUnavailable before bringToFront. If we display the inspector window via bringToFront first it causes
149 // the call to canAttachWindow to return the wrong result on Windows.
150 // Calling bringToFront first causes the visibleHeight of the inspected page to always return 0 immediately after.
151 // Thus if we call canAttachWindow first we can avoid this problem. This change does not cause any regressions on Mac.
152 setDockingUnavailable(!canAttachWindow());
154 m_frontendLoaded = true;
155 for (Vector<String>::iterator it = m_evaluateOnLoad.begin(); it != m_evaluateOnLoad.end(); ++it)
157 m_evaluateOnLoad.clear();
160 void InspectorFrontendClientLocal::requestSetDockSide(DockSide dockSide)
162 if (dockSide == UNDOCKED) {
164 setAttachedWindow(dockSide);
165 } else if (canAttachWindow()) {
166 attachWindow(dockSide);
167 setAttachedWindow(dockSide);
171 bool InspectorFrontendClientLocal::canAttachWindow()
173 // Don't allow attaching to another inspector -- two inspectors in one window is too much!
174 bool isInspectorPage = m_inspectorController->hasInspectorFrontendClient();
178 // If we are already attached, allow attaching again to allow switching sides.
179 if (m_dockSide != UNDOCKED)
182 // Don't allow the attach if the window would be too small to accommodate the minimum inspector size.
183 unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
184 unsigned inspectedPageWidth = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleWidth();
185 unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
186 return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
189 void InspectorFrontendClientLocal::setDockingUnavailable(bool unavailable)
191 evaluateOnLoad(String::format("[\"setDockingUnavailable\", %s]", unavailable ? "true" : "false"));
194 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
196 unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
197 unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
198 m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
199 setAttachedWindowHeight(attachedHeight);
202 void InspectorFrontendClientLocal::changeAttachedWindowWidth(unsigned width)
204 unsigned totalWidth = m_frontendPage->mainFrame()->view()->visibleWidth() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleWidth();
205 unsigned attachedWidth = constrainedAttachedWindowWidth(width, totalWidth);
206 setAttachedWindowWidth(attachedWidth);
209 void InspectorFrontendClientLocal::openInNewTab(const String& url)
211 UserGestureIndicator indicator(DefinitelyProcessingNewUserGesture);
212 Page* page = m_inspectorController->inspectedPage();
213 Frame* mainFrame = page->mainFrame();
214 FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
217 WindowFeatures windowFeatures;
218 RefPtr<Frame> frame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
222 frame->loader()->setOpener(mainFrame);
223 frame->page()->setOpenedByDOM();
225 // FIXME: Why does one use mainFrame and the other frame?
226 frame->loader()->changeLocation(mainFrame->document()->securityOrigin(), frame->document()->completeURL(url), "", false, false);
229 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
231 FloatRect frameRect = m_frontendPage->chrome().windowRect();
232 frameRect.move(x, y);
233 m_frontendPage->chrome().setWindowRect(frameRect);
236 void InspectorFrontendClientLocal::setAttachedWindow(DockSide dockSide)
238 const char* side = "undocked";
243 case DOCKED_TO_RIGHT:
246 case DOCKED_TO_BOTTOM:
251 m_dockSide = dockSide;
253 evaluateOnLoad(String::format("[\"setDockSide\", \"%s\"]", side));
256 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
258 unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
259 String value = m_settings->getProperty(inspectorAttachedHeightSetting);
260 unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
262 // This call might not go through (if the window starts out detached), but if the window is initially created attached,
263 // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
264 // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
265 setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
268 bool InspectorFrontendClientLocal::isDebuggingEnabled()
270 if (m_frontendLoaded)
271 return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
275 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
277 evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
280 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
282 if (m_frontendLoaded)
283 return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
287 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
289 evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
292 bool InspectorFrontendClientLocal::isProfilingJavaScript()
294 if (m_frontendLoaded)
295 return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
299 void InspectorFrontendClientLocal::startProfilingJavaScript()
301 evaluateOnLoad("[\"startProfilingJavaScript\"]");
304 void InspectorFrontendClientLocal::stopProfilingJavaScript()
306 evaluateOnLoad("[\"stopProfilingJavaScript\"]");
309 void InspectorFrontendClientLocal::showConsole()
311 evaluateOnLoad("[\"showConsole\"]");
314 void InspectorFrontendClientLocal::showResources()
316 evaluateOnLoad("[\"showResources\"]");
319 void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
321 String frameId = m_inspectorController->pageAgent()->frameId(frame);
322 evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
325 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
327 return roundf(std::max(minimumAttachedHeight, std::min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
330 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowWidth(unsigned preferredWidth, unsigned totalWindowWidth)
332 return roundf(std::max(minimumAttachedWidth, std::min<float>(preferredWidth, totalWindowWidth - minimumAttachedInspectedWidth)));
335 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
337 m_dispatchTask->dispatch(message);
340 bool InspectorFrontendClientLocal::isUnderTest()
342 return m_inspectorController->isUnderTest();
345 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
347 if (!m_frontendPage->mainFrame())
349 ScriptValue value = m_frontendPage->mainFrame()->script()->executeScript(expression);
350 return value.toString(mainWorldScriptState(m_frontendPage->mainFrame())) == "true";
353 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
355 if (m_frontendLoaded)
356 m_frontendPage->mainFrame()->script()->executeScript("InspectorFrontendAPI.dispatch(" + expression + ")");
358 m_evaluateOnLoad.append(expression);
361 } // namespace WebCore