Web Inspector: Network: add button to show system certificate dialog
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendHost.cpp
index 01705e8..b1773aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2017 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
  * 2.  Redistributions in binary form must reproduce the above copyright
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
- * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
  *     its contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
 #include "config.h"
 #include "InspectorFrontendHost.h"
 
-#if ENABLE(INSPECTOR)
-
+#include "CertificateInfo.h"
 #include "ContextMenu.h"
-#include "ContextMenuItem.h"
 #include "ContextMenuController.h"
+#include "ContextMenuItem.h"
 #include "ContextMenuProvider.h"
-#include "Element.h"
+#include "DOMWrapperWorld.h"
+#include "Document.h"
+#include "Editor.h"
+#include "Event.h"
+#include "FocusController.h"
 #include "Frame.h"
-#include "FrameLoader.h"
 #include "HitTestResult.h"
-#include "HTMLFrameOwnerElement.h"
-#include "InspectorAgent.h"
 #include "InspectorController.h"
 #include "InspectorFrontendClient.h"
+#include "JSDOMConvertInterface.h"
+#include "JSDOMExceptionHandling.h"
+#include "JSExecState.h"
+#include "JSInspectorFrontendHost.h"
+#include "MouseEvent.h"
+#include "Node.h"
 #include "Page.h"
 #include "Pasteboard.h"
-#include "ScriptFunctionCall.h"
+#include "ScriptState.h"
 #include "UserGestureIndicator.h"
-
-#include <wtf/RefPtr.h>
+#include <JavaScriptCore/ScriptFunctionCall.h>
+#include <pal/system/Sound.h>
 #include <wtf/StdLibExtras.h>
-
-using namespace std;
+#include <wtf/text/Base64.h>
 
 namespace WebCore {
 
+using namespace Inspector;
+
 #if ENABLE(CONTEXT_MENUS)
 class FrontendMenuProvider : public ContextMenuProvider {
 public:
-    static PassRefPtr<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, ScriptObject webInspector, const Vector<ContextMenuItem*>& items)
+    static Ref<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
     {
-        return adoptRef(new FrontendMenuProvider(frontendHost, webInspector, items));
+        return adoptRef(*new FrontendMenuProvider(frontendHost, frontendApiObject, items));
     }
     
     void disconnect()
     {
-        m_webInspector = ScriptObject();
-        m_frontendHost = 0;
+        m_frontendApiObject = { };
+        m_frontendHost = nullptr;
     }
     
 private:
-    FrontendMenuProvider(InspectorFrontendHost* frontendHost, ScriptObject webInspector,  const Vector<ContextMenuItem*>& items)
+    FrontendMenuProvider(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
         : m_frontendHost(frontendHost)
-        , m_webInspector(webInspector)
+        , m_frontendApiObject(frontendApiObject)
         , m_items(items)
     {
     }
@@ -83,39 +90,38 @@ private:
         contextMenuCleared();
     }
     
-    virtual void populateContextMenu(ContextMenu* menu)
+    void populateContextMenu(ContextMenu* menu) override
     {
-        for (size_t i = 0; i < m_items.size(); ++i)
-            menu->appendItem(*m_items[i]);
+        for (auto& item : m_items)
+            menu->appendItem(item);
     }
     
-    virtual void contextMenuItemSelected(ContextMenuItem* item)
+    void contextMenuItemSelected(ContextMenuAction action, const String&) override
     {
         if (m_frontendHost) {
-            UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
-            int itemNumber = item->action() - ContextMenuItemBaseCustomTag;
+            UserGestureIndicator gestureIndicator(ProcessingUserGesture);
+            int itemNumber = action - ContextMenuItemBaseCustomTag;
 
-            ScriptFunctionCall function(m_webInspector, "contextMenuItemSelected");
+            Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected", WebCore::functionCallHandlerFromAnyThread);
             function.appendArgument(itemNumber);
             function.call();
         }
     }
     
-    virtual void contextMenuCleared()
+    void contextMenuCleared() override
     {
         if (m_frontendHost) {
-            ScriptFunctionCall function(m_webInspector, "contextMenuCleared");
+            Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared", WebCore::functionCallHandlerFromAnyThread);
             function.call();
 
-            m_frontendHost->m_menuProvider = 0;
+            m_frontendHost->m_menuProvider = nullptr;
         }
-        deleteAllValues(m_items);
         m_items.clear();
     }
 
     InspectorFrontendHost* m_frontendHost;
-    ScriptObject m_webInspector;
-    Vector<ContextMenuItem*> m_items;
+    Deprecated::ScriptObject m_frontendApiObject;
+    Vector<ContextMenuItem> m_items;
 };
 #endif
 
@@ -123,7 +129,7 @@ InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Pa
     : m_client(client)
     , m_frontendPage(frontendPage)
 #if ENABLE(CONTEXT_MENUS)
-    , m_menuProvider(0)
+    , m_menuProvider(nullptr)
 #endif
 {
 }
@@ -135,36 +141,45 @@ InspectorFrontendHost::~InspectorFrontendHost()
 
 void InspectorFrontendHost::disconnectClient()
 {
-    m_client = 0;
+    m_client = nullptr;
 #if ENABLE(CONTEXT_MENUS)
     if (m_menuProvider)
         m_menuProvider->disconnect();
 #endif
-    m_frontendPage = 0;
-}
-
-void InspectorFrontendHost::loaded()
-{
-    if (m_client)
-        m_client->frontendLoaded();
+    m_frontendPage = nullptr;
 }
 
-void InspectorFrontendHost::requestAttachWindow()
+void InspectorFrontendHost::addSelfToGlobalObjectInWorld(DOMWrapperWorld& world)
 {
-    if (m_client)
-        m_client->requestAttachWindow();
+    auto& state = *execStateFromPage(world, m_frontendPage);
+    auto& vm = state.vm();
+    JSC::JSLockHolder lock(vm);
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
+    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
+    globalObject.putDirect(vm, JSC::Identifier::fromString(&vm, "InspectorFrontendHost"), toJS<IDLInterface<InspectorFrontendHost>>(state, globalObject, *this));
+    if (UNLIKELY(scope.exception()))
+        reportException(&state, scope.exception());
 }
 
-void InspectorFrontendHost::requestDetachWindow()
+void InspectorFrontendHost::loaded()
 {
     if (m_client)
-        m_client->requestDetachWindow();
+        m_client->frontendLoaded();
 }
 
 void InspectorFrontendHost::requestSetDockSide(const String& side)
 {
-    if (m_client)
-        m_client->requestSetDockSide(side);
+    if (!m_client)
+        return;
+    if (side == "undocked")
+        m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Undocked);
+    else if (side == "right")
+        m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Right);
+    else if (side == "left")
+        m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Left);
+    else if (side == "bottom")
+        m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Bottom);
 }
 
 void InspectorFrontendHost::closeWindow()
@@ -187,56 +202,144 @@ void InspectorFrontendHost::inspectedURLChanged(const String& newURL)
         m_client->inspectedURLChanged(newURL);
 }
 
+void InspectorFrontendHost::setZoomFactor(float zoom)
+{
+    if (m_frontendPage)
+        m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1);
+}
+
+float InspectorFrontendHost::zoomFactor()
+{
+    if (m_frontendPage)
+        return m_frontendPage->mainFrame().pageZoomFactor();
+
+    return 1.0;
+}
+
+String InspectorFrontendHost::userInterfaceLayoutDirection()
+{
+    if (m_client && m_client->userInterfaceLayoutDirection() == UserInterfaceLayoutDirection::RTL)
+        return "rtl"_s;
+
+    return "ltr"_s;
+}
+
 void InspectorFrontendHost::setAttachedWindowHeight(unsigned height)
 {
     if (m_client)
         m_client->changeAttachedWindowHeight(height);
 }
 
+void InspectorFrontendHost::setAttachedWindowWidth(unsigned width)
+{
+    if (m_client)
+        m_client->changeAttachedWindowWidth(width);
+}
+
+void InspectorFrontendHost::startWindowDrag()
+{
+    if (m_client)
+        m_client->startWindowDrag();
+}
+
 void InspectorFrontendHost::moveWindowBy(float x, float y) const
 {
     if (m_client)
         m_client->moveWindowBy(x, y);
 }
 
-void InspectorFrontendHost::setInjectedScriptForOrigin(const String& origin, const String& script)
+String InspectorFrontendHost::localizedStringsURL()
 {
-    ASSERT(m_frontendPage->inspectorController());
-    m_frontendPage->inspectorController()->setInjectedScriptForOrigin(origin, script);
+    return m_client ? m_client->localizedStringsURL() : String();
 }
 
-String InspectorFrontendHost::localizedStringsURL()
+String InspectorFrontendHost::backendCommandsURL()
+{
+    return m_client ? m_client->backendCommandsURL() : String();
+}
+
+String InspectorFrontendHost::debuggableType()
 {
-    return m_client ? m_client->localizedStringsURL() : "";
+    return m_client ? m_client->debuggableType() : String();
 }
 
-String InspectorFrontendHost::hiddenPanels()
+unsigned InspectorFrontendHost::inspectionLevel()
 {
-    return m_client ? m_client->hiddenPanels() : "";
+    return m_client ? m_client->inspectionLevel() : 1;
+}
+
+String InspectorFrontendHost::platform()
+{
+#if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
+    return "mac"_s;
+#elif OS(WINDOWS)
+    return "windows"_s;
+#elif OS(LINUX)
+    return "linux"_s;
+#elif OS(FREEBSD)
+    return "freebsd"_s;
+#elif OS(OPENBSD)
+    return "openbsd"_s;
+#else
+    return "unknown"_s;
+#endif
+}
+
+String InspectorFrontendHost::port()
+{
+#if PLATFORM(GTK)
+    return "gtk"_s;
+#else
+    return "unknown"_s;
+#endif
 }
 
 void InspectorFrontendHost::copyText(const String& text)
 {
-    Pasteboard::generalPasteboard()->writePlainText(text);
+    Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace);
+}
+
+void InspectorFrontendHost::killText(const String& text, bool shouldPrependToKillRing, bool shouldStartNewSequence)
+{
+    if (!m_frontendPage)
+        return;
+
+    Editor& editor = m_frontendPage->focusController().focusedOrMainFrame().editor();
+    editor.setStartNewKillRingSequence(shouldStartNewSequence);
+    Editor::KillRingInsertionMode insertionMode = shouldPrependToKillRing ? Editor::KillRingInsertionMode::PrependText : Editor::KillRingInsertionMode::AppendText;
+    editor.addTextToKillRing(text, insertionMode);
 }
 
 void InspectorFrontendHost::openInNewTab(const String& url)
 {
+    if (WebCore::protocolIsJavaScript(url))
+        return;
+
     if (m_client)
         m_client->openInNewTab(url);
 }
 
-bool InspectorFrontendHost::canSaveAs()
+bool InspectorFrontendHost::canSave()
 {
     if (m_client)
-        return m_client->canSaveAs();
+        return m_client->canSave();
     return false;
 }
 
-void InspectorFrontendHost::saveAs(const String& fileName, const String& content)
+void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs)
+{
+    if (m_client)
+        m_client->save(url, content, base64Encoded, forceSaveAs);
+}
+
+void InspectorFrontendHost::append(const String& url, const String& content)
 {
     if (m_client)
-        m_client->saveAs(fileName, content);
+        m_client->append(url, content);
+}
+
+void InspectorFrontendHost::close(const String&)
+{
 }
 
 void InspectorFrontendHost::sendMessageToBackend(const String& message)
@@ -246,34 +349,124 @@ void InspectorFrontendHost::sendMessageToBackend(const String& message)
 }
 
 #if ENABLE(CONTEXT_MENUS)
-void InspectorFrontendHost::showContextMenu(Event* event, const Vector<ContextMenuItem*>& items)
+
+static void populateContextMenu(Vector<InspectorFrontendHost::ContextMenuItem>&& items, ContextMenu& menu)
+{
+    for (auto& item : items) {
+        if (item.type == "separator") {
+            menu.appendItem({ SeparatorType, ContextMenuItemTagNoAction, { } });
+            continue;
+        }
+
+        if (item.type == "subMenu" && item.subItems) {
+            ContextMenu subMenu;
+            populateContextMenu(WTFMove(*item.subItems), subMenu);
+
+            menu.appendItem({ SubmenuType, ContextMenuItemTagNoAction, item.label, &subMenu });
+            continue;
+        }
+
+        auto type = item.type == "checkbox" ? CheckableActionType : ActionType;
+        auto action = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + item.id.value_or(0));
+        ContextMenuItem menuItem = { type, action, item.label };
+        if (item.enabled)
+            menuItem.setEnabled(*item.enabled);
+        if (item.checked)
+            menuItem.setChecked(*item.checked);
+        menu.appendItem(menuItem);
+    }
+}
+#endif
+
+void InspectorFrontendHost::showContextMenu(Event& event, Vector<ContextMenuItem>&& items)
 {
+#if ENABLE(CONTEXT_MENUS)
     ASSERT(m_frontendPage);
-    ScriptState* frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
-    ScriptObject webInspectorObj;
-    if (!ScriptGlobalObject::get(frontendScriptState, "WebInspector", webInspectorObj)) {
-        ASSERT_NOT_REACHED();
+
+    auto& state = *execStateFromPage(debuggerWorld(), m_frontendPage);
+    auto value = state.lexicalGlobalObject()->get(&state, JSC::Identifier::fromString(&state.vm(), "InspectorFrontendAPI"));
+    ASSERT(value);
+    ASSERT(value.isObject());
+    auto* frontendAPIObject = asObject(value);
+    
+    ContextMenu menu;
+    populateContextMenu(WTFMove(items), menu);
+
+    auto menuProvider = FrontendMenuProvider::create(this, { &state, frontendAPIObject }, menu.items());
+    m_menuProvider = menuProvider.ptr();
+    m_frontendPage->contextMenuController().showContextMenu(event, menuProvider);
+#else
+    UNUSED_PARAM(event);
+    UNUSED_PARAM(items);
+#endif
+}
+
+void InspectorFrontendHost::dispatchEventAsContextMenuEvent(Event& event)
+{
+#if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS)
+    if (!is<MouseEvent>(event))
         return;
-    }
-    RefPtr<FrontendMenuProvider> menuProvider = FrontendMenuProvider::create(this, webInspectorObj, items);
-    ContextMenuController* menuController = m_frontendPage->contextMenuController();
-    menuController->showContextMenu(event, menuProvider);
-    m_menuProvider = menuProvider.get();
+
+    auto& mouseEvent = downcast<MouseEvent>(event);
+    IntPoint mousePoint { mouseEvent.clientX(), mouseEvent.clientY() };
+    auto& frame = *downcast<Node>(mouseEvent.target())->document().frame();
+
+    m_frontendPage->contextMenuController().showContextMenuAt(frame, mousePoint);
+#else
+    UNUSED_PARAM(event);
+#endif
+}
+
+bool InspectorFrontendHost::isUnderTest()
+{
+    return m_client && m_client->isUnderTest();
+}
+
+void InspectorFrontendHost::unbufferedLog(const String& message)
+{
+    // This is used only for debugging inspector tests.
+    WTFLogAlways("%s", message.utf8().data());
+}
+
+void InspectorFrontendHost::beep()
+{
+    PAL::systemBeep();
 }
+
+void InspectorFrontendHost::inspectInspector()
+{
+    if (m_frontendPage)
+        m_frontendPage->inspectorController().show();
+}
+
+bool InspectorFrontendHost::supportsShowCertificate() const
+{
+#if PLATFORM(COCOA)
+    return true;
+#else
+    return false;
 #endif
+}
 
-String InspectorFrontendHost::loadResourceSynchronously(const String& url)
+bool InspectorFrontendHost::showCertificate(const String& serializedCertificate)
 {
-    ResourceRequest request(url);
-    request.setHTTPMethod("GET");
+    if (!m_client)
+        return false;
+
+    Vector<uint8_t> data;
+    if (!base64Decode(serializedCertificate, data))
+        return false;
 
-    Vector<char> data;
-    ResourceError error;
-    ResourceResponse response;
-    m_frontendPage->mainFrame()->loader()->loadResourceSynchronously(request, DoNotAllowStoredCredentials, error, response, data);
-    return String(data.data(), data.size());
+    CertificateInfo certificateInfo;
+    WTF::Persistence::Decoder decoder(data.data(), data.size());
+    if (!decoder.decode(certificateInfo))
+        return false;
+
+    if (certificateInfo.isEmpty())
+        return false;
+
+    m_client->showCertificate(certificateInfo);
+    return true;
 }
 
 } // namespace WebCore
-
-#endif // ENABLE(INSPECTOR)