chromium/chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_mac.mm

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_mac.h"

#include <memory>

#import "chrome/browser/renderer_host/chrome_render_widget_host_view_mac_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_cocoa.h"
#include "chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_remote_cocoa.h"
#include "chrome/browser/ui/cocoa/tab_contents/web_drag_bookmark_handler_mac.h"
#include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_menu_helper.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "chrome/browser/ui/views/sad_tab_view.h"
#include "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h"
#include "components/remote_cocoa/browser/window.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/drop_data.h"
#include "ui/views/widget/widget.h"

ChromeWebContentsViewDelegateViewsMac::ChromeWebContentsViewDelegateViewsMac(
    content::WebContents* web_contents)
    : ContextMenuDelegate(web_contents),
      web_contents_(web_contents),
      bookmark_handler_(new WebDragBookmarkHandlerMac) {
  ChromeWebContentsViewFocusHelper::CreateForWebContents(web_contents);
}

ChromeWebContentsViewDelegateViewsMac::
    ~ChromeWebContentsViewDelegateViewsMac() = default;

gfx::NativeWindow ChromeWebContentsViewDelegateViewsMac::GetNativeWindow() {
  Browser* browser = chrome::FindBrowserWithTab(web_contents_);
  return browser ? browser->window()->GetNativeWindow() : nullptr;
}

NSObject<RenderWidgetHostViewMacDelegate>*
ChromeWebContentsViewDelegateViewsMac::GetDelegateForHost(
    content::RenderWidgetHost* render_widget_host,
    bool is_popup) {
  // We don't need a delegate for popups since they don't have
  // overscroll.
  if (is_popup) {
    return nil;
  }
  return [[ChromeRenderWidgetHostViewMacDelegate alloc]
      initWithRenderWidgetHost:render_widget_host];
}

content::WebDragDestDelegate*
ChromeWebContentsViewDelegateViewsMac::GetDragDestDelegate() {
  return bookmark_handler_.get();
}

void ChromeWebContentsViewDelegateViewsMac::ShowContextMenu(
    content::RenderFrameHost& render_frame_host,
    const content::ContextMenuParams& params) {
  ShowMenu(BuildMenu(
      render_frame_host,
      AddContextMenuParamsPropertiesFromPreferences(web_contents_, params)));
}

void ChromeWebContentsViewDelegateViewsMac::StoreFocus() {
  GetFocusHelper()->StoreFocus();
}

bool ChromeWebContentsViewDelegateViewsMac::RestoreFocus() {
  return GetFocusHelper()->RestoreFocus();
}

void ChromeWebContentsViewDelegateViewsMac::ResetStoredFocus() {
  GetFocusHelper()->ResetStoredFocus();
}

bool ChromeWebContentsViewDelegateViewsMac::Focus() {
  return GetFocusHelper()->Focus();
}

bool ChromeWebContentsViewDelegateViewsMac::TakeFocus(bool reverse) {
  return GetFocusHelper()->TakeFocus(reverse);
}

void ChromeWebContentsViewDelegateViewsMac::OnPerformingDrop(
    const content::DropData& drop_data,
    DropCompletionCallback callback) {
  HandleOnPerformingDrop(web_contents_, drop_data, std::move(callback));
}

std::unique_ptr<RenderViewContextMenuBase>
ChromeWebContentsViewDelegateViewsMac::BuildMenu(
    content::RenderFrameHost& render_frame_host,
    const content::ContextMenuParams& params) {
  std::unique_ptr<RenderViewContextMenuMac> menu;
  if (remote_cocoa::IsWindowRemote(GetNativeWindow())) {
    menu = std::make_unique<RenderViewContextMenuMacRemoteCocoa>(
        render_frame_host, params, GetActiveRenderWidgetHostView());
  } else {
    gfx::NativeView parent_view =
        GetActiveRenderWidgetHostView()->GetNativeView();
    menu = std::make_unique<RenderViewContextMenuMacCocoa>(
        render_frame_host, params, parent_view.GetNativeNSView());
  }

  menu->Init();

  return menu;
}

void ChromeWebContentsViewDelegateViewsMac::ShowMenu(
    std::unique_ptr<RenderViewContextMenuBase> menu) {
  context_menu_ = std::move(menu);
  if (!context_menu_.get())
    return;

  // The renderer may send the "show context menu" message multiple times, one
  // for each right click mouse event it receives. Normally, this doesn't happen
  // because mouse events are not forwarded once the context menu is showing.
  // However, there's a race - the context menu may not yet be showing when
  // the second mouse event arrives. In this case, |ShowContextMenu()| will
  // get called multiple times - if so, don't create another context menu.
  // TODO(asvitkine): Fix the renderer so that it doesn't do this.
  if (web_contents_->IsShowingContextMenu())
    return;

  context_menu_->Show();
}

content::RenderWidgetHostView*
ChromeWebContentsViewDelegateViewsMac::GetActiveRenderWidgetHostView() const {
  return web_contents_->GetTopLevelRenderWidgetHostView();
}

ChromeWebContentsViewFocusHelper*
ChromeWebContentsViewDelegateViewsMac::GetFocusHelper() const {
  ChromeWebContentsViewFocusHelper* helper =
      ChromeWebContentsViewFocusHelper::FromWebContents(web_contents_);
  DCHECK(helper);
  return helper;
}

std::unique_ptr<content::WebContentsViewDelegate> CreateWebContentsViewDelegate(
    content::WebContents* web_contents) {
  return std::make_unique<ChromeWebContentsViewDelegateViewsMac>(web_contents);
}