REGRESSION(r179429): Can't type comments in Facebook
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendClientLocal.cpp
index 4812b2a..415ff7c 100644 (file)
 #include "config.h"
 #include "InspectorFrontendClientLocal.h"
 
-#if ENABLE(INSPECTOR)
-
 #include "Chrome.h"
+#include "DOMWrapperWorld.h"
+#include "Document.h"
 #include "FloatRect.h"
-#include "Frame.h"
+#include "FrameLoadRequest.h"
+#include "FrameLoader.h"
 #include "FrameView.h"
-#include "InspectorBackendDispatcher.h"
 #include "InspectorController.h"
 #include "InspectorFrontendHost.h"
+#include "InspectorPageAgent.h"
+#include "MainFrame.h"
 #include "Page.h"
-#include "PlatformString.h"
-#include "ScriptFunctionCall.h"
-#include "ScriptObject.h"
+#include "ScriptController.h"
+#include "ScriptGlobalObject.h"
+#include "ScriptState.h"
+#include "Settings.h"
+#include "Timer.h"
+#include "UserGestureIndicator.h"
+#include "WindowFeatures.h"
+#include <bindings/ScriptValue.h>
+#include <inspector/InspectorBackendDispatchers.h>
+#include <wtf/Deque.h>
+#include <wtf/text/CString.h>
+
+using namespace Inspector;
 
 namespace WebCore {
 
+static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
 static const unsigned defaultAttachedHeight = 300;
 static const float minimumAttachedHeight = 250.0f;
 static const float maximumAttachedHeightRatio = 0.75f;
+static const float minimumAttachedWidth = 750.0f;
+static const float minimumAttachedInspectedWidth = 320.0f;
+
+class InspectorBackendDispatchTask {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    InspectorBackendDispatchTask(InspectorController* inspectorController)
+        : m_inspectorController(inspectorController)
+        , m_timer(*this, &InspectorBackendDispatchTask::timerFired)
+    {
+    }
+
+    void dispatch(const String& message)
+    {
+        m_messages.append(message);
+        if (!m_timer.isActive())
+            m_timer.startOneShot(0);
+    }
+
+    void reset()
+    {
+        m_messages.clear();
+        m_timer.stop();
+    }
+
+    void timerFired()
+    {
+        if (!m_messages.isEmpty()) {
+            // Dispatch can lead to the timer destruction -> schedule the next shot first.
+            m_timer.startOneShot(0);
+            m_inspectorController->dispatchMessageFromFrontend(m_messages.takeFirst());
+        }
+    }
+
+private:
+    InspectorController* m_inspectorController;
+    Timer m_timer;
+    Deque<String> m_messages;
+};
 
-InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage)
+String InspectorFrontendClientLocal::Settings::getProperty(const String&)
+{
+    return String();
+}
+
+void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
+{
+}
+
+InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage, std::unique_ptr<Settings> settings)
     : m_inspectorController(inspectorController)
     , m_frontendPage(frontendPage)
-    , m_frontendScriptState(0)
+    , m_settings(WTF::move(settings))
+    , m_frontendLoaded(false)
+    , m_dockSide(DockSide::Undocked)
 {
+    m_frontendPage->settings().setAllowFileAccessFromFileURLs(true);
+    m_frontendPage->settings().setJavaScriptRuntimeFlags({
+        JSC::RuntimeFlags::SymbolEnabled
+    });
+    m_dispatchTask = std::make_unique<InspectorBackendDispatchTask>(inspectorController);
 }
 
 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
 {
     if (m_frontendHost)
         m_frontendHost->disconnectClient();
-    m_frontendScriptState = 0;
     m_frontendPage = 0;
     m_inspectorController = 0;
 }
-    
+
 void InspectorFrontendClientLocal::windowObjectCleared()
 {
-    // FIXME: don't keep reference to the script state
-    m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
+    if (m_frontendHost)
+        m_frontendHost->disconnectClient();
+    
+    JSC::ExecState* frontendExecState = execStateFromPage(debuggerWorld(), m_frontendPage);
     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
-    ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_frontendHost.get());
+    ScriptGlobalObject::set(frontendExecState, "InspectorFrontendHost", m_frontendHost.get());
 }
 
 void InspectorFrontendClientLocal::frontendLoaded()
 {
+    // Call setDockingUnavailable before bringToFront. If we display the inspector window via bringToFront first it causes
+    // the call to canAttachWindow to return the wrong result on Windows.
+    // Calling bringToFront first causes the visibleHeight of the inspected page to always return 0 immediately after. 
+    // Thus if we call canAttachWindow first we can avoid this problem. This change does not cause any regressions on Mac.
+    setDockingUnavailable(!canAttachWindow());
     bringToFront();
-    m_inspectorController->connectFrontend();
+    m_frontendLoaded = true;
+    for (Vector<String>::iterator it = m_evaluateOnLoad.begin(); it != m_evaluateOnLoad.end(); ++it)
+        evaluateOnLoad(*it);
+    m_evaluateOnLoad.clear();
 }
 
-void InspectorFrontendClientLocal::requestAttachWindow()
+void InspectorFrontendClientLocal::requestSetDockSide(DockSide dockSide)
 {
-    if (!canAttachWindow())
-        return;
-    attachWindow();
-    setAttachedWindow(true);
+    if (dockSide == DockSide::Undocked) {
+        detachWindow();
+        setAttachedWindow(dockSide);
+    } else if (canAttachWindow()) {
+        attachWindow(dockSide);
+        setAttachedWindow(dockSide);
+    }
 }
 
-void InspectorFrontendClientLocal::requestDetachWindow()
+bool InspectorFrontendClientLocal::canAttachWindow()
 {
-    detachWindow();
-    setAttachedWindow(false);
+    // Don't allow attaching to another inspector -- two inspectors in one window is too much!
+    bool isInspectorPage = m_inspectorController->hasInspectorFrontendClient();
+    if (isInspectorPage)
+        return false;
+
+    // If we are already attached, allow attaching again to allow switching sides.
+    if (m_dockSide != DockSide::Undocked)
+        return true;
+
+    // Don't allow the attach if the window would be too small to accommodate the minimum inspector size.
+    unsigned inspectedPageHeight = m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
+    unsigned inspectedPageWidth = m_inspectorController->inspectedPage().mainFrame().view()->visibleWidth();
+    unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
+    return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
 }
 
-bool InspectorFrontendClientLocal::canAttachWindow()
+void InspectorFrontendClientLocal::setDockingUnavailable(bool unavailable)
 {
-    unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
-
-    // Don't allow the attach if the window would be too small to accommodate the minimum inspector height.
-    return minimumAttachedHeight <= inspectedPageHeight * maximumAttachedHeightRatio;
+    evaluateOnLoad(String::format("[\"setDockingUnavailable\", %s]", unavailable ? "true" : "false"));
 }
 
 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
 {
-    unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
+    unsigned totalHeight = m_frontendPage->mainFrame().view()->visibleHeight() + m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
-    m_inspectorController->setInspectorAttachedHeight(attachedHeight);
+    m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
     setAttachedWindowHeight(attachedHeight);
 }
 
+void InspectorFrontendClientLocal::changeAttachedWindowWidth(unsigned width)
+{
+    unsigned totalWidth = m_frontendPage->mainFrame().view()->visibleWidth() + m_inspectorController->inspectedPage().mainFrame().view()->visibleWidth();
+    unsigned attachedWidth = constrainedAttachedWindowWidth(width, totalWidth);
+    setAttachedWindowWidth(attachedWidth);
+}
+
+void InspectorFrontendClientLocal::openInNewTab(const String& url)
+{
+    UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
+    Frame& mainFrame = m_inspectorController->inspectedPage().mainFrame();
+    FrameLoadRequest request(mainFrame.document()->securityOrigin(), ResourceRequest(), "_blank");
+
+    bool created;
+    WindowFeatures windowFeatures;
+    RefPtr<Frame> frame = WebCore::createWindow(&mainFrame, &mainFrame, request, windowFeatures, created);
+    if (!frame)
+        return;
+
+    frame->loader().setOpener(&mainFrame);
+    frame->page()->setOpenedByDOM();
+
+    // FIXME: Why does one use mainFrame and the other frame?
+    frame->loader().changeLocation(mainFrame.document()->securityOrigin(), frame->document()->completeURL(url), "", LockHistory::No, LockBackForwardList::No);
+}
+
 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
 {
-    FloatRect frameRect = m_frontendPage->chrome()->windowRect();
+    FloatRect frameRect = m_frontendPage->chrome().windowRect();
     frameRect.move(x, y);
-    m_frontendPage->chrome()->setWindowRect(frameRect);
+    m_frontendPage->chrome().setWindowRect(frameRect);
 }
 
-void InspectorFrontendClientLocal::setAttachedWindow(bool attached)
+void InspectorFrontendClientLocal::setAttachedWindow(DockSide dockSide)
 {
-    ScriptObject webInspectorObj;
-    if (!ScriptGlobalObject::get(m_frontendScriptState, "WebInspector", webInspectorObj)) {
-        ASSERT_NOT_REACHED();
-        return;
+    const char* side = "undocked";
+    switch (dockSide) {
+    case DockSide::Undocked:
+        side = "undocked";
+        break;
+    case DockSide::Right:
+        side = "right";
+        break;
+    case DockSide::Bottom:
+        side = "bottom";
+        break;
     }
-    ScriptFunctionCall function(webInspectorObj, "setAttachedWindow");
-    function.appendArgument(attached);
-    function.call();
+
+    m_dockSide = dockSide;
+
+    evaluateOnLoad(String::format("[\"setDockSide\", \"%s\"]", side));
 }
 
 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
 {
-    unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
-    int attachedHeight = m_inspectorController->inspectorAttachedHeight();
-    bool success = true;
-    unsigned preferredHeight = success ? static_cast<unsigned>(attachedHeight) : defaultAttachedHeight;
+    unsigned inspectedPageHeight = m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
+    String value = m_settings->getProperty(inspectorAttachedHeightSetting);
+    unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
     
     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
@@ -143,17 +265,95 @@ void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
 }
 
+bool InspectorFrontendClientLocal::isDebuggingEnabled()
+{
+    if (m_frontendLoaded)
+        return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
+    return false;
+}
+
+void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
+{
+    evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
+}
+
+bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
+{
+    if (m_frontendLoaded)
+        return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
+    return false;
+}
+
+void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
+{
+    evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
+}
+
+bool InspectorFrontendClientLocal::isProfilingJavaScript()
+{
+    if (m_frontendLoaded)
+        return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
+    return false;
+}
+
+void InspectorFrontendClientLocal::startProfilingJavaScript()
+{
+    evaluateOnLoad("[\"startProfilingJavaScript\"]");
+}
+
+void InspectorFrontendClientLocal::stopProfilingJavaScript()
+{
+    evaluateOnLoad("[\"stopProfilingJavaScript\"]");
+}
+
+void InspectorFrontendClientLocal::showConsole()
+{
+    evaluateOnLoad("[\"showConsole\"]");
+}
+
+void InspectorFrontendClientLocal::showResources()
+{
+    evaluateOnLoad("[\"showResources\"]");
+}
+
+void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
+{
+    String frameId = m_inspectorController->pageAgent()->frameId(frame);
+    evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
+}
+
 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
 {
-    using namespace std;
-    return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
+    return roundf(std::max(minimumAttachedHeight, std::min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
+}
+
+unsigned InspectorFrontendClientLocal::constrainedAttachedWindowWidth(unsigned preferredWidth, unsigned totalWindowWidth)
+{
+    return roundf(std::max(minimumAttachedWidth, std::min<float>(preferredWidth, totalWindowWidth - minimumAttachedInspectedWidth)));
 }
 
 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
 {
-    m_inspectorController->inspectorBackendDispatcher()->dispatch(message);
+    m_dispatchTask->dispatch(message);
 }
 
-} // namespace WebCore
+bool InspectorFrontendClientLocal::isUnderTest()
+{
+    return m_inspectorController->isUnderTest();
+}
+
+bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
+{
+    Deprecated::ScriptValue value = m_frontendPage->mainFrame().script().executeScript(expression);
+    return value.toString(mainWorldExecState(&m_frontendPage->mainFrame())) == "true";
+}
 
-#endif
+void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
+{
+    if (m_frontendLoaded)
+        m_frontendPage->mainFrame().script().executeScript("InspectorFrontendAPI.dispatch(" + expression + ")");
+    else
+        m_evaluateOnLoad.append(expression);
+}
+
+} // namespace WebCore