910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "headless/lib/browser/protocol/browser_handler.h"

#include "base/functional/bind.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "headless/lib/browser/headless_browser_impl.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/lib/browser/protocol/target_handler.h"
#include "headless/public/headless_window_state.h"

namespace headless {
namespace protocol {

namespace {

std::unique_ptr<Browser::Bounds> CreateBrowserBounds(
    const HeadlessWebContentsImpl* web_contents) {
  gfx::Rect bounds = web_contents->web_contents()->GetContainerBounds();
  return Browser::Bounds::Create()
      .SetLeft(bounds.x())
      .SetTop(bounds.y())
      .SetWidth(bounds.width())
      .SetHeight(bounds.height())
      .SetWindowState(GetProtocolWindowState(web_contents->GetWindowState()))
      .Build();
}

}  // namespace

BrowserHandler::BrowserHandler(HeadlessBrowserImpl* browser,
                               const std::string& target_id)
    : browser_(browser), target_id_(target_id) {}

BrowserHandler::~BrowserHandler() = default;

void BrowserHandler::Wire(UberDispatcher* dispatcher) {
  Browser::Dispatcher::wire(dispatcher, this);
}

Response BrowserHandler::Disable() {
  return Response::Success();
}

Response BrowserHandler::GetWindowForTarget(
    std::optional<std::string> target_id,
    int* out_window_id,
    std::unique_ptr<Browser::Bounds>* out_bounds) {
  auto agent_host =
      content::DevToolsAgentHost::GetForId(target_id.value_or(target_id_));
  HeadlessWebContentsImpl* web_contents =
      HeadlessWebContentsImpl::From(agent_host->GetWebContents());
  if (!web_contents)
    return Response::ServerError("No web contents for the given target id");

  *out_window_id = web_contents->window_id();
  *out_bounds = CreateBrowserBounds(web_contents);
  return Response::Success();
}

Response BrowserHandler::GetWindowBounds(
    int window_id,
    std::unique_ptr<Browser::Bounds>* out_bounds) {
  HeadlessWebContentsImpl* web_contents =
      browser_->GetWebContentsForWindowId(window_id);
  if (!web_contents)
    return Response::ServerError("Browser window not found");
  *out_bounds = CreateBrowserBounds(web_contents);
  return Response::Success();
}

Response BrowserHandler::Close() {
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&HeadlessBrowserImpl::Shutdown, browser_->GetWeakPtr()));
  return Response::Success();
}

Response BrowserHandler::SetWindowBounds(
    int window_id,
    std::unique_ptr<Browser::Bounds> window_bounds) {
  HeadlessWebContentsImpl* web_contents =
      browser_->GetWebContentsForWindowId(window_id);
  if (!web_contents)
    return Response::ServerError("Browser window not found");

  const bool set_bounds = window_bounds->HasLeft() || window_bounds->HasTop() ||
                          window_bounds->HasWidth() ||
                          window_bounds->HasHeight();

  std::optional<HeadlessWindowState> headless_window_state;
  if (window_bounds->HasWindowState()) {
    std::string protocol_window_state = window_bounds->GetWindowState().value();
    headless_window_state = GetWindowStateFromProtocol(protocol_window_state);
    if (!headless_window_state) {
      return Response::InvalidParams("Invalid window state: " +
                                     protocol_window_state);
    }

    if (set_bounds && headless_window_state != HeadlessWindowState::kNormal) {
      return Response::InvalidParams(
          "The 'minimized', 'maximized' and 'fullscreen' states cannot be "
          "combined with 'left', 'top', 'width' or 'height'");
    }
  }

  if (set_bounds &&
      web_contents->GetWindowState() != HeadlessWindowState::kNormal) {
    return Response::ServerError(
        "To resize minimized/maximized/fullscreen window, restore it to normal "
        "state first.");
  }

  // Set the requested or normal window state. Note that this will update window
  // position according to the requested window state if necessary.
  web_contents->SetWindowState(
      headless_window_state.value_or(HeadlessWindowState::kNormal));

  if (set_bounds) {
    gfx::Rect bounds = web_contents->web_contents()->GetContainerBounds();

    bounds.set_x(window_bounds->GetLeft(bounds.x()));
    bounds.set_y(window_bounds->GetTop(bounds.y()));
    bounds.set_width(window_bounds->GetWidth(bounds.width()));
    bounds.set_height(window_bounds->GetHeight(bounds.height()));

    web_contents->SetBounds(bounds);
  }

  return Response::Success();
}

Response BrowserHandler::SetContentsSize(int window_id,
                                         std::optional<int> width,
                                         std::optional<int> height) {
  HeadlessWebContentsImpl* web_contents =
      browser_->GetWebContentsForWindowId(window_id);
  if (!web_contents) {
    return Response::ServerError("Browser window not found");
  }

  if (web_contents->GetWindowState() != HeadlessWindowState::kNormal) {
    return Response::ServerError(
        "Restore window to normal state before setting content size");
  }

  if (!width && !height) {
    return Response::InvalidParams(
        "At least one of 'width' or 'height' must be specified");
  }

  if (width && width.value() <= 0) {
    return Response::InvalidParams("Contents 'width' must be a positive value");
  }

  if (height && height.value() <= 0) {
    return Response::InvalidParams(
        "Contents 'height' must be a positive value");
  }

  gfx::Rect bounds = web_contents->web_contents()->GetContainerBounds();
  bounds.set_width(width.value_or(bounds.width()));
  bounds.set_height(height.value_or(bounds.height()));

  web_contents->SetBounds(bounds);

  return Response::Success();
}

protocol::Response BrowserHandler::SetDockTile(
    std::optional<std::string> label,
    std::optional<protocol::Binary> image) {
  return Response::Success();
}

}  // namespace protocol
}  // namespace headless