text: /*
   * Copyright (C) 2010 Apple Inc. All rights reserved.
   * Copyright (C) 2017 Sony Interactive Entertainment Inc.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 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.
   *
   * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   * THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "config.h"
  #include "WebInspectorProxy.h"
  
  #include "APINavigation.h"
  #include "APINavigationAction.h"
  #include "APIPageConfiguration.h"
  #include "PageClientImpl.h"
  #include "WebFramePolicyListenerProxy.h"
  #include "WebPageGroup.h"
  #include "WebPageProxy.h"
  #include "WebPreferences.h"
  #include "WebProcessPool.h"
  #include "WebView.h"
  #include <WebCore/CertificateInfo.h>
  #include <WebCore/InspectorFrontendClientLocal.h>
  #include <WebCore/NotImplemented.h>
  #include <WebCore/WebCoreBundleWin.h>
  #include <WebCore/WebCoreInstanceHandle.h>
  #include <WebCore/WindowMessageBroadcaster.h>
  #include <WebKit/WKPage.h>
  
  #if USE(CF)
  #include <wtf/cf/CFURLExtras.h>
  #endif
  
  namespace WebKit {
  
  static const LPCWSTR WebInspectorProxyPointerProp = L"WebInspectorProxyPointer";
  static const LPCWSTR WebInspectorProxyClassName = L"WebInspectorProxyClass";
  
  struct InspectedWindowInfo {
      int left;
      int top;
      int viewWidth;
      int viewHeight;
      int parentWidth;
      int parentHeight;
  };
  
  static InspectedWindowInfo getInspectedWindowInfo(HWND inspectedWindow, HWND parentWindow)
  {
      RECT rect;
      ::GetClientRect(inspectedWindow, &rect);
      ::MapWindowPoints(inspectedWindow, parentWindow, (LPPOINT)&rect, 2);
  
      RECT parentRect;
      ::GetClientRect(parentWindow, &parentRect);
      return { rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, parentRect.right - parentRect.left, parentRect.bottom - parentRect.top };
  }
  
  void WebInspectorProxy::windowReceivedMessage(HWND hwnd, UINT msg, WPARAM, LPARAM lParam)
  {
      switch (msg) {
      case WM_WINDOWPOSCHANGING: {
          if (!m_isAttached)
              return;
  
          auto windowPos = reinterpret_cast<WINDOWPOS*>(lParam);
  
          if (windowPos->flags & SWP_NOSIZE)
              return;
  
          HWND parent = GetParent(hwnd);
          RECT parentRect;
          GetClientRect(parent, &parentRect);
  
          RECT inspectorRect;
          GetClientRect(m_inspectorViewWindow, &inspectorRect);
  
          switch (m_attachmentSide) {
          case AttachmentSide::Bottom: {
              unsigned inspectorHeight = WebCore::InspectorFrontendClientLocal::constrainedAttachedWindowHeight(inspectorRect.bottom - inspectorRect.top, windowPos->cy);
              windowPos->cy -= inspectorHeight;
              ::SetWindowPos(m_inspectorViewWindow, 0, windowPos->x, windowPos->y + windowPos->cy, windowPos->cx, inspectorHeight, SWP_NOZORDER);
              break;
          }
          case AttachmentSide::Left:
          case AttachmentSide::Right: {
              unsigned inspectorWidth = WebCore::InspectorFrontendClientLocal::constrainedAttachedWindowWidth(inspectorRect.right - inspectorRect.left, windowPos->cx);
              windowPos->cx -= inspectorWidth;
              ::SetWindowPos(m_inspectorViewWindow, 0, windowPos->x + windowPos->cx, windowPos->y, inspectorWidth, windowPos->cy, SWP_NOZORDER);
              break;
          }
          default:
              break;
          }
          break;
      }
      default:
          break;
      }
  }
  
  LRESULT CALLBACK WebInspectorProxy::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
      WebInspectorProxy* client = reinterpret_cast<WebInspectorProxy*>(::GetProp(hwnd, WebInspectorProxyPointerProp));
      switch (msg) {
      case WM_SIZE:
          ::SetWindowPos(client->m_inspectorViewWindow, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER);
          return 0;
      case WM_CLOSE:
          client->close();
          return 0;
      default:
          break;
      }
      return ::DefWindowProc(hwnd, msg, wParam, lParam);
  }
  
  bool WebInspectorProxy::registerWindowClass()
  {
      static bool haveRegisteredWindowClass = false;
  
      if (haveRegisteredWindowClass)
          return true;
      haveRegisteredWindowClass = true;
  
      WNDCLASSEX wcex;
      wcex.cbSize = sizeof(WNDCLASSEX);
      wcex.style = 0;
      wcex.lpfnWndProc = wndProc;
      wcex.cbClsExtra = 0;
      wcex.cbWndExtra = 0;
      wcex.hInstance = WebCore::instanceHandle();
      wcex.hIcon = 0;
      wcex.hCursor = LoadCursor(0, IDC_ARROW);
      wcex.hbrBackground = 0;
      wcex.lpszMenuName = 0;
      wcex.lpszClassName = WebInspectorProxyClassName;
      wcex.hIconSm = 0;
      return ::RegisterClassEx(&wcex);
  }
  
  static void decidePolicyForNavigationAction(WKPageRef pageRef, WKNavigationActionRef navigationActionRef, WKFramePolicyListenerRef listenerRef, WKTypeRef, const void* clientInfo)
  {
      // Allow non-main frames to navigate anywhere.
      API::FrameInfo* sourceFrame = toImpl(navigationActionRef)->sourceFrame();
      if (sourceFrame && !sourceFrame->isMainFrame()) {
          toImpl(listenerRef)->use({ });
          return;
      }
  
      const WebInspectorProxy* webInspectorProxy = static_cast<const WebInspectorProxy*>(clientInfo);
      ASSERT(webInspectorProxy);
  
      WebCore::ResourceRequest request = toImpl(navigationActionRef)->request();
  
      // Allow loading of the main inspector file.
      if (WebInspectorProxy::isMainOrTestInspectorPage(request.url())) {
          toImpl(listenerRef)->use({ });
          return;
      }
  
      // Prevent everything else from loading in the inspector's page.
      toImpl(listenerRef)->ignore();
  
      // And instead load it in the inspected page.
      webInspectorProxy->inspectedPage()->loadRequest(WTFMove(request));
  }
  
  static void webProcessDidCrash(WKPageRef, const void* clientInfo)
  {
      WebInspectorProxy* webInspectorProxy = static_cast<WebInspectorProxy*>(const_cast<void*>(clientInfo));
      ASSERT(webInspectorProxy);
      webInspectorProxy->closeForCrash();
  }
  
  WebPageProxy* WebInspectorProxy::platformCreateFrontendPage()
  {
      ASSERT(inspectedPage());
  
      auto preferences = WebPreferences::create(String(), "WebKit2.", "WebKit2.");
  #if ENABLE(DEVELOPER_MODE)
      // Allow developers to inspect the Web Inspector in debug builds without changing settings.
      preferences->setDeveloperExtrasEnabled(true);
      preferences->setLogsPageMessagesToSystemConsoleEnabled(true);
  #endif
      preferences->setAllowFileAccessFromFileURLs(true);
      preferences->setJavaScriptRuntimeFlags({ });
      auto pageGroup = WebPageGroup::create(inspectorPageGroupIdentifierForPage(inspectedPage()));
      auto pageConfiguration = API::PageConfiguration::create();
      pageConfiguration->setProcessPool(&inspectorProcessPool(inspectionLevel()));
      pageConfiguration->setPreferences(preferences.ptr());
      pageConfiguration->setPageGroup(pageGroup.ptr());
  
      WKPageNavigationClientV0 navigationClient = {
          { 0, this },
          decidePolicyForNavigationAction,
          nullptr, // decidePolicyForNavigationResponse
          nullptr, // decidePolicyForPluginLoad
          nullptr, // didStartProvisionalNavigation
          nullptr, // didReceiveServerRedirectForProvisionalNavigation
          nullptr, // didFailProvisionalNavigation
          nullptr, // didCommitNavigation
          nullptr, // didFinishNavigation
          nullptr, // didFailNavigation
          nullptr, // didFailProvisionalLoadInSubframe
          nullptr, // didFinishDocumentLoad
          nullptr, // didSameDocumentNavigation
          nullptr, // renderingProgressDidChange
          nullptr, // canAuthenticateAgainstProtectionSpace
          nullptr, // didReceiveAuthenticationChallenge
          webProcessDidCrash,
          nullptr, // copyWebCryptoMasterKey
  
          nullptr, // didBeginNavigationGesture
          nullptr, // willEndNavigationGesture
          nullptr, // didEndNavigationGesture
          nullptr, // didRemoveNavigationGestureSnapshot
      };
  
      RECT r = { 0, 0, static_cast<LONG>(initialWindowWidth), static_cast<LONG>(initialWindowHeight) };
      auto page = inspectedPage();
      m_inspectedViewWindow = page->viewWidget();
      m_inspectedViewParentWindow = ::GetParent(m_inspectedViewWindow);
      auto view = WebView::create(r, pageConfiguration, m_inspectedViewParentWindow);
      m_inspectorView = &view.leakRef();
      auto inspectorPage = m_inspectorView->page();
      m_inspectorViewWindow = inspectorPage->viewWidget();
      WKPageSetPageNavigationClient(toAPI(inspectorPage), &navigationClient.base);
  
      return inspectorPage;
  }
  
  void WebInspectorProxy::platformCloseFrontendPageAndWindow()
  {
      WebCore::WindowMessageBroadcaster::removeListener(m_inspectedViewWindow, this);
      m_inspectorView = nullptr;
      m_inspectorPage = nullptr;
      if (m_inspectorViewWindow) {
          ::DestroyWindow(m_inspectorViewWindow);
          m_inspectorViewWindow = nullptr;
      }
      if (m_inspectorDetachWindow) {
          ::RemoveProp(m_inspectorDetachWindow, WebInspectorProxyPointerProp);
          ::DestroyWindow(m_inspectorDetachWindow);
          m_inspectorDetachWindow = nullptr;
      }
      m_inspectedViewWindow = nullptr;
      m_inspectedViewParentWindow = nullptr;
  }
  
  String WebInspectorProxy::inspectorPageURL()
  {
  #if USE(CF)
      RetainPtr<CFURLRef> htmlURLRef = adoptCF(CFBundleCopyResourceURL(WebCore::webKitBundle(), CFSTR("Main"), CFSTR("html"), CFSTR("WebInspectorUI")));
      return CFURLGetString(htmlURLRef.get());
  #else
      return { };
  #endif
  }
  
  String WebInspectorProxy::inspectorTestPageURL()
  {
  #if USE(CF)
      RetainPtr<CFURLRef> htmlURLRef = adoptCF(CFBundleCopyResourceURL(WebCore::webKitBundle(), CFSTR("Test"), CFSTR("html"), CFSTR("WebInspectorUI")));
      return CFURLGetString(htmlURLRef.get());
  #else
      return { };
  #endif
  }
  
  String WebInspectorProxy::inspectorBaseURL()
  {
  #if USE(CF)
      RetainPtr<CFURLRef> baseURLRef = adoptCF(CFBundleCopyResourceURL(WebCore::webKitBundle(), CFSTR("WebInspectorUI"), nullptr, nullptr));
      return CFURLGetString(baseURLRef.get());
  #else
      return { };
  #endif
  }
  
  DebuggableInfoData WebInspectorProxy::infoForLocalDebuggable()
  {
      // FIXME <https://webkit.org/b/205537>: this should infer more useful data.
      return DebuggableInfoData::empty();
  }
  
  unsigned WebInspectorProxy::platformInspectedWindowHeight()
  {
      RECT rect;
      ::GetClientRect(m_inspectedViewWindow, &rect);
      return rect.bottom - rect.top;
  }
  
  unsigned WebInspectorProxy::platformInspectedWindowWidth()
  {
      RECT rect;
      ::GetClientRect(m_inspectedViewWindow, &rect);
      return rect.right - rect.left;
  }
  
  void WebInspectorProxy::platformAttach()
  {
      static const unsigned defaultAttachedSize = 300;
      static const unsigned minimumAttachedWidth = 750;
      static const unsigned minimumAttachedHeight = 250;
  
      if (m_inspectorDetachWindow && ::GetParent(m_inspectorViewWindow) == m_inspectorDetachWindow) {
          ::SetParent(m_inspectorViewWindow, m_inspectedViewParentWindow);
          ::ShowWindow(m_inspectorDetachWindow, SW_HIDE);
      }
  
      WebCore::WindowMessageBroadcaster::addListener(m_inspectedViewWindow, this);
  
      if (m_attachmentSide == AttachmentSide::Bottom) {
          unsigned maximumAttachedHeight = platformInspectedWindowHeight() * 3 / 4;
          platformSetAttachedWindowHeight(std::max(minimumAttachedHeight, std::min(defaultAttachedSize, maximumAttachedHeight)));
      } else {
          unsigned maximumAttachedWidth = platformInspectedWindowWidth() * 3 / 4;
          platformSetAttachedWindowWidth(std::max(minimumAttachedWidth, std::min(defaultAttachedSize, maximumAttachedWidth)));
      }
      ::ShowWindow(m_inspectorViewWindow, SW_SHOW);
  }
  
  void WebInspectorProxy::platformDetach()
  {
      if (!inspectedPage()->hasRunningProcess())
          return;
  
      if (!m_inspectorDetachWindow) {
          registerWindowClass();
          m_inspectorDetachWindow = ::CreateWindowEx(0, WebInspectorProxyClassName, 0, WS_OVERLAPPEDWINDOW,
              CW_USEDEFAULT, CW_USEDEFAULT, initialWindowWidth, initialWindowHeight,
              0, 0, WebCore::instanceHandle(), 0);
          ::SetProp(m_inspectorDetachWindow, WebInspectorProxyPointerProp, reinterpret_cast<HANDLE>(this));
      }
  
      WebCore::WindowMessageBroadcaster::removeListener(m_inspectedViewWindow, this);
  
      RECT rect;
      ::GetClientRect(m_inspectorDetachWindow, &rect);
      auto windowInfo = getInspectedWindowInfo(m_inspectedViewWindow, m_inspectedViewParentWindow);
      ::SetParent(m_inspectorViewWindow, m_inspectorDetachWindow);
      ::SetWindowPos(m_inspectorViewWindow, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
      ::SetWindowPos(m_inspectedViewWindow, 0, windowInfo.left, windowInfo.top, windowInfo.parentWidth - windowInfo.left, windowInfo.parentHeight - windowInfo.top, SWP_NOZORDER);
  
      if (m_isVisible)
          ::ShowWindow(m_inspectorDetachWindow, SW_SHOW);
  }
  
  void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned height)
  {
      auto windowInfo = getInspectedWindowInfo(m_inspectedViewWindow, m_inspectedViewParentWindow);
      ::SetWindowPos(m_inspectorViewWindow, 0, windowInfo.left, windowInfo.parentHeight - height, windowInfo.parentWidth - windowInfo.left, height, SWP_NOZORDER);
      ::SetWindowPos(m_inspectedViewWindow, 0, windowInfo.left, windowInfo.top, windowInfo.parentWidth - windowInfo.left, windowInfo.parentHeight - windowInfo.top, SWP_NOZORDER);
  }
  
  void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned width)
  {
      auto windowInfo = getInspectedWindowInfo(m_inspectedViewWindow, m_inspectedViewParentWindow);
      ::SetWindowPos(m_inspectorViewWindow, 0, windowInfo.parentWidth - width, windowInfo.top, width, windowInfo.parentHeight - windowInfo.top, SWP_NOZORDER);
      ::SetWindowPos(m_inspectedViewWindow, 0, windowInfo.left, windowInfo.top, windowInfo.parentWidth - windowInfo.left, windowInfo.parentHeight - windowInfo.top, SWP_NOZORDER);
  }
  
  void WebInspectorProxy::platformSetSheetRect(const WebCore::FloatRect&)
  {
      notImplemented();
  }
  
  bool WebInspectorProxy::platformIsFront()
  {
      notImplemented();
      return false;
  }
  
  void WebInspectorProxy::platformHide()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformResetState()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformBringToFront()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformBringInspectedPageToFront()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformInspectedURLChanged(const String& /* url */)
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformShowCertificate(const WebCore::CertificateInfo&)
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformSave(const String&, const String&, bool, bool)
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformAppend(const String&, const String&)
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformAttachAvailabilityChanged(bool /* available */)
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformCreateFrontendWindow()
  {
      platformDetach();
  }
  
  void WebInspectorProxy::platformDidCloseForCrash()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformInvalidate()
  {
      notImplemented();
  }
  
  void WebInspectorProxy::platformStartWindowDrag()
  {
      notImplemented();
  }
  
  }
  