mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Pass a new |popup_id| parameter to OnBeforePopup and call a new OnBeforePopupAborted callback if the popup is aborted before OnAfterCreated is called for the popup browser. Add new CefBrowserHost::GetBrowserByIdentifier and GetOpenerIdentifier methods to assist with retrieval of associated browsers. In cefclient, clean up state when a popup is aborted and close any associated popup browsers when the opener browser is closed. This also works when running with `--use-default-popup`.
		
			
				
	
	
		
			1313 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1313 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
 | 
						|
// reserved. Use of this source code is governed by a BSD-style license that
 | 
						|
// can be found in the LICENSE file.
 | 
						|
 | 
						|
#include "tests/cefclient/browser/root_window_win.h"
 | 
						|
 | 
						|
#include <shellscalingapi.h>
 | 
						|
 | 
						|
#include <memory>
 | 
						|
#include <optional>
 | 
						|
 | 
						|
#include "include/base/cef_build.h"
 | 
						|
#include "include/base/cef_callback.h"
 | 
						|
#include "include/cef_app.h"
 | 
						|
#include "include/views/cef_display.h"
 | 
						|
#include "tests/cefclient/browser/browser_window_osr_win.h"
 | 
						|
#include "tests/cefclient/browser/browser_window_std_win.h"
 | 
						|
#include "tests/cefclient/browser/client_prefs.h"
 | 
						|
#include "tests/cefclient/browser/main_context.h"
 | 
						|
#include "tests/cefclient/browser/resource.h"
 | 
						|
#include "tests/cefclient/browser/temp_window.h"
 | 
						|
#include "tests/cefclient/browser/window_test_runner_win.h"
 | 
						|
#include "tests/shared/browser/geometry_util.h"
 | 
						|
#include "tests/shared/browser/main_message_loop.h"
 | 
						|
#include "tests/shared/browser/util_win.h"
 | 
						|
#include "tests/shared/common/client_switches.h"
 | 
						|
 | 
						|
#define MAX_URL_LENGTH 255
 | 
						|
#define BUTTON_WIDTH 72
 | 
						|
#define URLBAR_HEIGHT 24
 | 
						|
 | 
						|
namespace client {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
// Message handler for the About box.
 | 
						|
INT_PTR CALLBACK AboutWndProc(HWND hDlg,
 | 
						|
                              UINT message,
 | 
						|
                              WPARAM wParam,
 | 
						|
                              LPARAM lParam) {
 | 
						|
  UNREFERENCED_PARAMETER(lParam);
 | 
						|
  switch (message) {
 | 
						|
    case WM_INITDIALOG:
 | 
						|
      return TRUE;
 | 
						|
 | 
						|
    case WM_COMMAND:
 | 
						|
      if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
 | 
						|
        EndDialog(hDlg, LOWORD(wParam));
 | 
						|
        return TRUE;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
// Returns true if the process is per monitor DPI aware.
 | 
						|
bool IsProcessPerMonitorDpiAware() {
 | 
						|
  enum class PerMonitorDpiAware {
 | 
						|
    UNKNOWN = 0,
 | 
						|
    PER_MONITOR_DPI_UNAWARE,
 | 
						|
    PER_MONITOR_DPI_AWARE,
 | 
						|
  };
 | 
						|
  static PerMonitorDpiAware per_monitor_dpi_aware = PerMonitorDpiAware::UNKNOWN;
 | 
						|
  if (per_monitor_dpi_aware == PerMonitorDpiAware::UNKNOWN) {
 | 
						|
    per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_UNAWARE;
 | 
						|
    HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll");
 | 
						|
    if (shcore_dll) {
 | 
						|
      typedef HRESULT(WINAPI * GetProcessDpiAwarenessPtr)(
 | 
						|
          HANDLE, PROCESS_DPI_AWARENESS*);
 | 
						|
      GetProcessDpiAwarenessPtr func_ptr =
 | 
						|
          reinterpret_cast<GetProcessDpiAwarenessPtr>(
 | 
						|
              ::GetProcAddress(shcore_dll, "GetProcessDpiAwareness"));
 | 
						|
      if (func_ptr) {
 | 
						|
        PROCESS_DPI_AWARENESS awareness;
 | 
						|
        if (SUCCEEDED(func_ptr(nullptr, &awareness)) &&
 | 
						|
            awareness == PROCESS_PER_MONITOR_DPI_AWARE) {
 | 
						|
          per_monitor_dpi_aware = PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return per_monitor_dpi_aware == PerMonitorDpiAware::PER_MONITOR_DPI_AWARE;
 | 
						|
}
 | 
						|
 | 
						|
// DPI value for 1x scale factor.
 | 
						|
#define DPI_1X 96.0f
 | 
						|
 | 
						|
float GetWindowScaleFactor(HWND hwnd) {
 | 
						|
  if (hwnd && IsProcessPerMonitorDpiAware()) {
 | 
						|
    typedef UINT(WINAPI * GetDpiForWindowPtr)(HWND);
 | 
						|
    static GetDpiForWindowPtr func_ptr = reinterpret_cast<GetDpiForWindowPtr>(
 | 
						|
        GetProcAddress(GetModuleHandle(L"user32.dll"), "GetDpiForWindow"));
 | 
						|
    if (func_ptr) {
 | 
						|
      return static_cast<float>(func_ptr(hwnd)) / DPI_1X;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return client::GetDeviceScaleFactor();
 | 
						|
}
 | 
						|
 | 
						|
int GetButtonWidth(HWND hwnd) {
 | 
						|
  return LogicalToDevice(BUTTON_WIDTH, GetWindowScaleFactor(hwnd));
 | 
						|
}
 | 
						|
 | 
						|
int GetURLBarHeight(HWND hwnd) {
 | 
						|
  return LogicalToDevice(URLBAR_HEIGHT, GetWindowScaleFactor(hwnd));
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
RootWindowWin::RootWindowWin(bool use_alloy_style)
 | 
						|
    : RootWindow(use_alloy_style) {
 | 
						|
  // Create a HRGN representing the draggable window area.
 | 
						|
  draggable_region_ = ::CreateRectRgn(0, 0, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
RootWindowWin::~RootWindowWin() {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  ::DeleteObject(draggable_region_);
 | 
						|
  ::DeleteObject(font_);
 | 
						|
 | 
						|
  // The window and browser should already have been destroyed.
 | 
						|
  DCHECK(window_destroyed_);
 | 
						|
  DCHECK(browser_destroyed_);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::Init(RootWindow::Delegate* delegate,
 | 
						|
                         std::unique_ptr<RootWindowConfig> config,
 | 
						|
                         const CefBrowserSettings& settings) {
 | 
						|
  DCHECK(delegate);
 | 
						|
  DCHECK(!initialized_);
 | 
						|
 | 
						|
  delegate_ = delegate;
 | 
						|
  with_controls_ = config->with_controls;
 | 
						|
  always_on_top_ = config->always_on_top;
 | 
						|
  with_osr_ = config->with_osr;
 | 
						|
 | 
						|
  CreateBrowserWindow(config->url);
 | 
						|
 | 
						|
  if (CefCurrentlyOn(TID_UI)) {
 | 
						|
    ContinueInitOnUIThread(std::move(config), settings);
 | 
						|
  } else {
 | 
						|
    CefPostTask(TID_UI, base::BindOnce(&RootWindowWin::ContinueInitOnUIThread,
 | 
						|
                                       this, std::move(config), settings));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::ContinueInitOnUIThread(
 | 
						|
    std::unique_ptr<RootWindowConfig> config,
 | 
						|
    const CefBrowserSettings& settings) {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  if (!config->bounds.IsEmpty()) {
 | 
						|
    // Initial state was specified via the config object.
 | 
						|
    initial_bounds_ = config->bounds;
 | 
						|
    initial_show_state_ = config->show_state;
 | 
						|
  } else {
 | 
						|
    // Initial state may be specified via the command-line or global
 | 
						|
    // preferences.
 | 
						|
    std::optional<CefRect> bounds;
 | 
						|
    if (prefs::LoadWindowRestorePreferences(initial_show_state_, bounds) &&
 | 
						|
        bounds) {
 | 
						|
      initial_bounds_ = CefDisplay::ConvertScreenRectToPixels(*bounds);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MAIN_POST_CLOSURE(base::BindOnce(&RootWindowWin::ContinueInitOnMainThread,
 | 
						|
                                   this, std::move(config), settings));
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::ContinueInitOnMainThread(
 | 
						|
    std::unique_ptr<RootWindowConfig> config,
 | 
						|
    const CefBrowserSettings& settings) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  initialized_ = true;
 | 
						|
 | 
						|
  CreateRootWindow(settings, config->initially_hidden);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::InitAsPopup(RootWindow::Delegate* delegate,
 | 
						|
                                bool with_controls,
 | 
						|
                                bool with_osr,
 | 
						|
                                const CefPopupFeatures& popupFeatures,
 | 
						|
                                CefWindowInfo& windowInfo,
 | 
						|
                                CefRefPtr<CefClient>& client,
 | 
						|
                                CefBrowserSettings& settings) {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  DCHECK(delegate);
 | 
						|
  DCHECK(!initialized_);
 | 
						|
 | 
						|
  delegate_ = delegate;
 | 
						|
  with_controls_ = with_controls;
 | 
						|
  with_osr_ = with_osr;
 | 
						|
  is_popup_ = true;
 | 
						|
 | 
						|
  if (popupFeatures.xSet) {
 | 
						|
    initial_bounds_.x = popupFeatures.x;
 | 
						|
  }
 | 
						|
  if (popupFeatures.ySet) {
 | 
						|
    initial_bounds_.y = popupFeatures.y;
 | 
						|
  }
 | 
						|
  if (popupFeatures.widthSet) {
 | 
						|
    initial_bounds_.width = popupFeatures.width;
 | 
						|
  }
 | 
						|
  if (popupFeatures.heightSet) {
 | 
						|
    initial_bounds_.height = popupFeatures.height;
 | 
						|
  }
 | 
						|
 | 
						|
  CreateBrowserWindow(std::string());
 | 
						|
 | 
						|
  initialized_ = true;
 | 
						|
 | 
						|
  // The new popup is initially parented to a temporary window. The native root
 | 
						|
  // window will be created after the browser is created and the popup window
 | 
						|
  // will be re-parented to it at that time.
 | 
						|
  browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo,
 | 
						|
                                  client, settings);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::Show(ShowMode mode) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (!hwnd_) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  int nCmdShow = SW_SHOWNORMAL;
 | 
						|
  switch (mode) {
 | 
						|
    case ShowMinimized:
 | 
						|
      nCmdShow = SW_SHOWMINIMIZED;
 | 
						|
      break;
 | 
						|
    case ShowMaximized:
 | 
						|
      nCmdShow = SW_SHOWMAXIMIZED;
 | 
						|
      break;
 | 
						|
    case ShowNoActivate:
 | 
						|
      nCmdShow = SW_SHOWNOACTIVATE;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  ShowWindow(hwnd_, nCmdShow);
 | 
						|
  if (mode != ShowMinimized) {
 | 
						|
    UpdateWindow(hwnd_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::Hide() {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (hwnd_) {
 | 
						|
    ShowWindow(hwnd_, SW_HIDE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (hwnd_) {
 | 
						|
    SetWindowPos(hwnd_, nullptr, x, y, static_cast<int>(width),
 | 
						|
                 static_cast<int>(height), SWP_NOZORDER);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::Close(bool force) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (hwnd_) {
 | 
						|
    if (force) {
 | 
						|
      DestroyWindow(hwnd_);
 | 
						|
    } else {
 | 
						|
      PostMessage(hwnd_, WM_CLOSE, 0, 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::SetDeviceScaleFactor(float device_scale_factor) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (browser_window_ && with_osr_) {
 | 
						|
    browser_window_->SetDeviceScaleFactor(device_scale_factor);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
float RootWindowWin::GetDeviceScaleFactor() const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (browser_window_ && with_osr_) {
 | 
						|
    return browser_window_->GetDeviceScaleFactor();
 | 
						|
  }
 | 
						|
 | 
						|
  NOTREACHED();
 | 
						|
  return 0.0f;
 | 
						|
}
 | 
						|
 | 
						|
CefRefPtr<CefBrowser> RootWindowWin::GetBrowser() const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (browser_window_) {
 | 
						|
    return browser_window_->GetBrowser();
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
ClientWindowHandle RootWindowWin::GetWindowHandle() const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
  return hwnd_;
 | 
						|
}
 | 
						|
 | 
						|
bool RootWindowWin::WithWindowlessRendering() const {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
  return with_osr_;
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::CreateBrowserWindow(const std::string& startup_url) {
 | 
						|
  if (with_osr_) {
 | 
						|
    OsrRendererSettings settings = {};
 | 
						|
    MainContext::Get()->PopulateOsrSettings(&settings);
 | 
						|
    browser_window_ = std::make_unique<BrowserWindowOsrWin>(
 | 
						|
        this, with_controls_, startup_url, settings);
 | 
						|
  } else {
 | 
						|
    browser_window_ = std::make_unique<BrowserWindowStdWin>(
 | 
						|
        this, with_controls_, startup_url);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
 | 
						|
                                     bool initially_hidden) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
  DCHECK(!hwnd_);
 | 
						|
 | 
						|
  HINSTANCE hInstance = GetModuleHandle(nullptr);
 | 
						|
 | 
						|
  // Load strings from the resource file.
 | 
						|
  const std::wstring& window_title = GetResourceString(IDS_APP_TITLE);
 | 
						|
  const std::wstring& window_class = GetResourceString(IDR_MAINFRAME);
 | 
						|
 | 
						|
  const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
 | 
						|
  const HBRUSH background_brush = CreateSolidBrush(
 | 
						|
      RGB(CefColorGetR(background_color), CefColorGetG(background_color),
 | 
						|
          CefColorGetB(background_color)));
 | 
						|
 | 
						|
  // Register the window class.
 | 
						|
  RegisterRootClass(hInstance, window_class, background_brush);
 | 
						|
 | 
						|
  // Register the message used with the find dialog.
 | 
						|
  find_message_id_ = RegisterWindowMessage(FINDMSGSTRING);
 | 
						|
  CHECK(find_message_id_);
 | 
						|
 | 
						|
  CefRefPtr<CefCommandLine> command_line =
 | 
						|
      CefCommandLine::GetGlobalCommandLine();
 | 
						|
  const bool no_activate = command_line->HasSwitch(switches::kNoActivate);
 | 
						|
 | 
						|
  DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
 | 
						|
  DWORD dwExStyle = always_on_top_ ? WS_EX_TOPMOST : 0;
 | 
						|
  if (no_activate) {
 | 
						|
    // Don't activate the browser window on creation.
 | 
						|
    dwExStyle |= WS_EX_NOACTIVATE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (initial_show_state_ == CEF_SHOW_STATE_MAXIMIZED) {
 | 
						|
    dwStyle |= WS_MAXIMIZE;
 | 
						|
  } else if (initial_show_state_ == CEF_SHOW_STATE_MINIMIZED) {
 | 
						|
    dwStyle |= WS_MINIMIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  int x, y, width, height;
 | 
						|
  if (initial_bounds_.IsEmpty()) {
 | 
						|
    // Use the default window position/size.
 | 
						|
    x = y = width = height = CW_USEDEFAULT;
 | 
						|
  } else {
 | 
						|
    x = initial_bounds_.x;
 | 
						|
    y = initial_bounds_.y;
 | 
						|
    width = initial_bounds_.width;
 | 
						|
    height = initial_bounds_.height;
 | 
						|
 | 
						|
    if (is_popup_) {
 | 
						|
      // Adjust the window size to account for window frame and controls. Keep
 | 
						|
      // the origin unchanged.
 | 
						|
      RECT window_rect = {x, y, x + width, y + height};
 | 
						|
      ::AdjustWindowRectEx(&window_rect, dwStyle, with_controls_, dwExStyle);
 | 
						|
      width = window_rect.right - window_rect.left;
 | 
						|
      height = window_rect.bottom - window_rect.top;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  browser_settings_ = settings;
 | 
						|
 | 
						|
  // Create the main window initially hidden.
 | 
						|
  CreateWindowEx(dwExStyle, window_class.c_str(), window_title.c_str(), dwStyle,
 | 
						|
                 x, y, width, height, nullptr, nullptr, hInstance, this);
 | 
						|
  CHECK(hwnd_);
 | 
						|
 | 
						|
  if (!called_enable_non_client_dpi_scaling_ && IsProcessPerMonitorDpiAware()) {
 | 
						|
    // This call gets Windows to scale the non-client area when WM_DPICHANGED
 | 
						|
    // is fired on Windows versions < 10.0.14393.0.
 | 
						|
    // Derived signature; not available in headers.
 | 
						|
    typedef LRESULT(WINAPI * EnableChildWindowDpiMessagePtr)(HWND, BOOL);
 | 
						|
    static EnableChildWindowDpiMessagePtr func_ptr =
 | 
						|
        reinterpret_cast<EnableChildWindowDpiMessagePtr>(GetProcAddress(
 | 
						|
            GetModuleHandle(L"user32.dll"), "EnableChildWindowDpiMessage"));
 | 
						|
    if (func_ptr) {
 | 
						|
      func_ptr(hwnd_, TRUE);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!initially_hidden) {
 | 
						|
    ShowMode mode = ShowNormal;
 | 
						|
    if (no_activate) {
 | 
						|
      mode = ShowNoActivate;
 | 
						|
    } else if (initial_show_state_ == CEF_SHOW_STATE_MAXIMIZED) {
 | 
						|
      mode = ShowMaximized;
 | 
						|
    } else if (initial_show_state_ == CEF_SHOW_STATE_MINIMIZED) {
 | 
						|
      mode = ShowMinimized;
 | 
						|
    }
 | 
						|
 | 
						|
    // Show this window.
 | 
						|
    Show(mode);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
void RootWindowWin::RegisterRootClass(HINSTANCE hInstance,
 | 
						|
                                      const std::wstring& window_class,
 | 
						|
                                      HBRUSH background_brush) {
 | 
						|
  // Only register the class one time.
 | 
						|
  static bool class_registered = false;
 | 
						|
  if (class_registered) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  class_registered = true;
 | 
						|
 | 
						|
  WNDCLASSEX wcex;
 | 
						|
 | 
						|
  wcex.cbSize = sizeof(WNDCLASSEX);
 | 
						|
 | 
						|
  wcex.style = CS_HREDRAW | CS_VREDRAW;
 | 
						|
  wcex.lpfnWndProc = RootWndProc;
 | 
						|
  wcex.cbClsExtra = 0;
 | 
						|
  wcex.cbWndExtra = 0;
 | 
						|
  wcex.hInstance = hInstance;
 | 
						|
  wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME));
 | 
						|
  wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
 | 
						|
  wcex.hbrBackground = background_brush;
 | 
						|
  wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAINFRAME);
 | 
						|
  wcex.lpszClassName = window_class.c_str();
 | 
						|
  wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
 | 
						|
 | 
						|
  RegisterClassEx(&wcex);
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
LRESULT CALLBACK RootWindowWin::EditWndProc(HWND hWnd,
 | 
						|
                                            UINT message,
 | 
						|
                                            WPARAM wParam,
 | 
						|
                                            LPARAM lParam) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
 | 
						|
  DCHECK(self);
 | 
						|
  DCHECK(hWnd == self->edit_hwnd_);
 | 
						|
 | 
						|
  switch (message) {
 | 
						|
    case WM_CHAR:
 | 
						|
      if (wParam == VK_RETURN) {
 | 
						|
        // When the user hits the enter key load the URL.
 | 
						|
        CefRefPtr<CefBrowser> browser = self->GetBrowser();
 | 
						|
        if (browser) {
 | 
						|
          wchar_t strPtr[MAX_URL_LENGTH + 1] = {0};
 | 
						|
          *((LPWORD)strPtr) = MAX_URL_LENGTH;
 | 
						|
          LRESULT strLen = SendMessage(hWnd, EM_GETLINE, 0, (LPARAM)strPtr);
 | 
						|
          if (strLen > 0) {
 | 
						|
            strPtr[strLen] = 0;
 | 
						|
            browser->GetMainFrame()->LoadURL(strPtr);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case WM_NCDESTROY:
 | 
						|
      // Clear the reference to |self|.
 | 
						|
      SetUserDataPtr(hWnd, nullptr);
 | 
						|
      self->edit_hwnd_ = nullptr;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return CallWindowProc(self->edit_wndproc_old_, hWnd, message, wParam, lParam);
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
LRESULT CALLBACK RootWindowWin::FindWndProc(HWND hWnd,
 | 
						|
                                            UINT message,
 | 
						|
                                            WPARAM wParam,
 | 
						|
                                            LPARAM lParam) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowWin* self = GetUserDataPtr<RootWindowWin*>(hWnd);
 | 
						|
  DCHECK(self);
 | 
						|
  DCHECK(hWnd == self->find_hwnd_);
 | 
						|
 | 
						|
  switch (message) {
 | 
						|
    case WM_ACTIVATE:
 | 
						|
      // Set this dialog as current when activated.
 | 
						|
      MainMessageLoop::Get()->SetCurrentModelessDialog(wParam == 0 ? nullptr
 | 
						|
                                                                   : hWnd);
 | 
						|
      return FALSE;
 | 
						|
    case WM_NCDESTROY:
 | 
						|
      // Clear the reference to |self|.
 | 
						|
      SetUserDataPtr(hWnd, nullptr);
 | 
						|
      self->find_hwnd_ = nullptr;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return CallWindowProc(self->find_wndproc_old_, hWnd, message, wParam, lParam);
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd,
 | 
						|
                                            UINT message,
 | 
						|
                                            WPARAM wParam,
 | 
						|
                                            LPARAM lParam) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  RootWindowWin* self = nullptr;
 | 
						|
  if (message != WM_NCCREATE) {
 | 
						|
    self = GetUserDataPtr<RootWindowWin*>(hWnd);
 | 
						|
    if (!self) {
 | 
						|
      return DefWindowProc(hWnd, message, wParam, lParam);
 | 
						|
    }
 | 
						|
    DCHECK_EQ(hWnd, self->hwnd_);
 | 
						|
  }
 | 
						|
 | 
						|
  if (self && message == self->find_message_id_) {
 | 
						|
    // Message targeting the find dialog.
 | 
						|
    LPFINDREPLACE lpfr = reinterpret_cast<LPFINDREPLACE>(lParam);
 | 
						|
    CHECK(lpfr == &self->find_state_);
 | 
						|
    self->OnFindEvent();
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  // Callback for the main window
 | 
						|
  switch (message) {
 | 
						|
    case WM_COMMAND:
 | 
						|
      if (self->OnCommand(LOWORD(wParam))) {
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_GETOBJECT: {
 | 
						|
      // Only the lower 32 bits of lParam are valid when checking the object id
 | 
						|
      // because it sometimes gets sign-extended incorrectly (but not always).
 | 
						|
      DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
 | 
						|
 | 
						|
      // Accessibility readers will send an OBJID_CLIENT message.
 | 
						|
      if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
 | 
						|
        if (self->GetBrowser() && self->GetBrowser()->GetHost()) {
 | 
						|
          self->GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } break;
 | 
						|
 | 
						|
    case WM_PAINT:
 | 
						|
      self->OnPaint();
 | 
						|
      return 0;
 | 
						|
 | 
						|
    case WM_ACTIVATE:
 | 
						|
      self->OnActivate(LOWORD(wParam) != WA_INACTIVE);
 | 
						|
      // Allow DefWindowProc to set keyboard focus.
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_SETFOCUS:
 | 
						|
      self->OnFocus();
 | 
						|
      return 0;
 | 
						|
 | 
						|
    case WM_ENABLE:
 | 
						|
      if (wParam == TRUE) {
 | 
						|
        // Give focus to the browser after EnableWindow enables this window
 | 
						|
        // (e.g. after a modal dialog is dismissed).
 | 
						|
        self->OnFocus();
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_SIZE:
 | 
						|
      self->OnSize(wParam == SIZE_MINIMIZED);
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_MOVING:
 | 
						|
    case WM_MOVE:
 | 
						|
      self->OnMove();
 | 
						|
      return 0;
 | 
						|
 | 
						|
    case WM_DPICHANGED:
 | 
						|
      self->OnDpiChanged(wParam, lParam);
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_ERASEBKGND:
 | 
						|
      if (self->OnEraseBkgnd()) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      // Don't erase the background.
 | 
						|
      return 0;
 | 
						|
 | 
						|
    case WM_ENTERMENULOOP:
 | 
						|
      if (!wParam) {
 | 
						|
        // Entering the menu loop for the application menu.
 | 
						|
        CefSetOSModalLoop(true);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_EXITMENULOOP:
 | 
						|
      if (!wParam) {
 | 
						|
        // Exiting the menu loop for the application menu.
 | 
						|
        CefSetOSModalLoop(false);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_CLOSE:
 | 
						|
      if (self->OnClose()) {
 | 
						|
        return 0;  // Cancel the close.
 | 
						|
      }
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_NCHITTEST: {
 | 
						|
      LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
 | 
						|
      if (hit == HTCLIENT) {
 | 
						|
        POINTS points = MAKEPOINTS(lParam);
 | 
						|
        POINT point = {points.x, points.y};
 | 
						|
        ::ScreenToClient(hWnd, &point);
 | 
						|
        if (::PtInRegion(self->draggable_region_, point.x, point.y)) {
 | 
						|
          // If cursor is inside a draggable region return HTCAPTION to allow
 | 
						|
          // dragging.
 | 
						|
          return HTCAPTION;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return hit;
 | 
						|
    }
 | 
						|
 | 
						|
    case WM_NCCREATE: {
 | 
						|
      CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
 | 
						|
      self = reinterpret_cast<RootWindowWin*>(cs->lpCreateParams);
 | 
						|
      DCHECK(self);
 | 
						|
      // Associate |self| with the main window.
 | 
						|
      SetUserDataPtr(hWnd, self);
 | 
						|
      self->hwnd_ = hWnd;
 | 
						|
 | 
						|
      self->OnNCCreate(cs);
 | 
						|
    } break;
 | 
						|
 | 
						|
    case WM_CREATE:
 | 
						|
      self->OnCreate(reinterpret_cast<CREATESTRUCT*>(lParam));
 | 
						|
      break;
 | 
						|
 | 
						|
    case WM_NCDESTROY:
 | 
						|
      // Clear the reference to |self|.
 | 
						|
      SetUserDataPtr(hWnd, nullptr);
 | 
						|
      self->hwnd_ = nullptr;
 | 
						|
      self->OnDestroyed();
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return DefWindowProc(hWnd, message, wParam, lParam);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnPaint() {
 | 
						|
  PAINTSTRUCT ps;
 | 
						|
  BeginPaint(hwnd_, &ps);
 | 
						|
  EndPaint(hwnd_, &ps);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnFocus() {
 | 
						|
  // Selecting "Close window" from the task bar menu may send a focus
 | 
						|
  // notification even though the window is currently disabled (e.g. while a
 | 
						|
  // modal JS dialog is displayed).
 | 
						|
  if (browser_window_ && ::IsWindowEnabled(hwnd_)) {
 | 
						|
    browser_window_->SetFocus(true);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnActivate(bool active) {
 | 
						|
  if (active) {
 | 
						|
    delegate_->OnRootWindowActivated(this);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnSize(bool minimized) {
 | 
						|
  if (minimized) {
 | 
						|
    // Notify the browser window that it was hidden and do nothing further.
 | 
						|
    if (browser_window_) {
 | 
						|
      browser_window_->Hide();
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (browser_window_) {
 | 
						|
    browser_window_->Show();
 | 
						|
  }
 | 
						|
 | 
						|
  RECT rect;
 | 
						|
  GetClientRect(hwnd_, &rect);
 | 
						|
 | 
						|
  if (with_controls_ && edit_hwnd_) {
 | 
						|
    const int button_width = GetButtonWidth(hwnd_);
 | 
						|
    const int urlbar_height = GetURLBarHeight(hwnd_);
 | 
						|
    const int font_height = LogicalToDevice(14, GetWindowScaleFactor(hwnd_));
 | 
						|
 | 
						|
    if (font_height != font_height_) {
 | 
						|
      font_height_ = font_height;
 | 
						|
      if (font_) {
 | 
						|
        DeleteObject(font_);
 | 
						|
      }
 | 
						|
 | 
						|
      // Create a scaled font.
 | 
						|
      font_ =
 | 
						|
          ::CreateFont(-font_height, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
 | 
						|
                       DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
 | 
						|
                       DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
 | 
						|
 | 
						|
      SendMessage(back_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
 | 
						|
                  TRUE);
 | 
						|
      SendMessage(forward_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
 | 
						|
                  TRUE);
 | 
						|
      SendMessage(reload_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
 | 
						|
                  TRUE);
 | 
						|
      SendMessage(stop_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
 | 
						|
                  TRUE);
 | 
						|
      SendMessage(edit_hwnd_, WM_SETFONT, reinterpret_cast<WPARAM>(font_),
 | 
						|
                  TRUE);
 | 
						|
    }
 | 
						|
 | 
						|
    // Resize the window and address bar to match the new frame size.
 | 
						|
    rect.top += urlbar_height;
 | 
						|
 | 
						|
    int x_offset = rect.left;
 | 
						|
 | 
						|
    // |browser_hwnd| may be nullptr if the browser has not yet been created.
 | 
						|
    HWND browser_hwnd = nullptr;
 | 
						|
    if (browser_window_) {
 | 
						|
      browser_hwnd = browser_window_->GetWindowHandle();
 | 
						|
    }
 | 
						|
 | 
						|
    // Resize all controls.
 | 
						|
    HDWP hdwp = BeginDeferWindowPos(browser_hwnd ? 6 : 5);
 | 
						|
    hdwp = DeferWindowPos(hdwp, back_hwnd_, nullptr, x_offset, 0, button_width,
 | 
						|
                          urlbar_height, SWP_NOZORDER);
 | 
						|
    x_offset += button_width;
 | 
						|
    hdwp = DeferWindowPos(hdwp, forward_hwnd_, nullptr, x_offset, 0,
 | 
						|
                          button_width, urlbar_height, SWP_NOZORDER);
 | 
						|
    x_offset += button_width;
 | 
						|
    hdwp = DeferWindowPos(hdwp, reload_hwnd_, nullptr, x_offset, 0,
 | 
						|
                          button_width, urlbar_height, SWP_NOZORDER);
 | 
						|
    x_offset += button_width;
 | 
						|
    hdwp = DeferWindowPos(hdwp, stop_hwnd_, nullptr, x_offset, 0, button_width,
 | 
						|
                          urlbar_height, SWP_NOZORDER);
 | 
						|
    x_offset += button_width;
 | 
						|
    hdwp = DeferWindowPos(hdwp, edit_hwnd_, nullptr, x_offset, 0,
 | 
						|
                          rect.right - x_offset, urlbar_height, SWP_NOZORDER);
 | 
						|
 | 
						|
    if (browser_hwnd) {
 | 
						|
      hdwp = DeferWindowPos(hdwp, browser_hwnd, nullptr, rect.left, rect.top,
 | 
						|
                            rect.right - rect.left, rect.bottom - rect.top,
 | 
						|
                            SWP_NOZORDER);
 | 
						|
    }
 | 
						|
 | 
						|
    [[maybe_unused]] BOOL result = EndDeferWindowPos(hdwp);
 | 
						|
    DCHECK(result);
 | 
						|
  } else if (browser_window_) {
 | 
						|
    // Size the browser window to the whole client area.
 | 
						|
    browser_window_->SetBounds(0, 0, rect.right, rect.bottom);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnMove() {
 | 
						|
  // Notify the browser of move events so that popup windows are displayed
 | 
						|
  // in the correct location and dismissed when the window moves.
 | 
						|
  CefRefPtr<CefBrowser> browser = GetBrowser();
 | 
						|
  if (browser) {
 | 
						|
    browser->GetHost()->NotifyMoveOrResizeStarted();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnDpiChanged(WPARAM wParam, LPARAM lParam) {
 | 
						|
  if (LOWORD(wParam) != HIWORD(wParam)) {
 | 
						|
    NOTIMPLEMENTED() << "Received non-square scaling factors";
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (browser_window_ && with_osr_) {
 | 
						|
    // Scale factor for the new display.
 | 
						|
    const float display_scale_factor =
 | 
						|
        static_cast<float>(LOWORD(wParam)) / DPI_1X;
 | 
						|
    browser_window_->SetDeviceScaleFactor(display_scale_factor);
 | 
						|
  }
 | 
						|
 | 
						|
  // Suggested size and position of the current window scaled for the new DPI.
 | 
						|
  const RECT* rect = reinterpret_cast<RECT*>(lParam);
 | 
						|
  SetBounds(rect->left, rect->top, rect->right - rect->left,
 | 
						|
            rect->bottom - rect->top);
 | 
						|
}
 | 
						|
 | 
						|
bool RootWindowWin::OnEraseBkgnd() {
 | 
						|
  // Erase the background when the browser does not exist.
 | 
						|
  return (GetBrowser() == nullptr);
 | 
						|
}
 | 
						|
 | 
						|
bool RootWindowWin::OnCommand(UINT id) {
 | 
						|
  if (id >= ID_TESTS_FIRST && id <= ID_TESTS_LAST) {
 | 
						|
    delegate_->OnTest(this, id);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (id) {
 | 
						|
    case IDM_ABOUT:
 | 
						|
      OnAbout();
 | 
						|
      return true;
 | 
						|
    case IDM_EXIT:
 | 
						|
      delegate_->OnExit(this);
 | 
						|
      return true;
 | 
						|
    case ID_FIND:
 | 
						|
      OnFind();
 | 
						|
      return true;
 | 
						|
    case IDC_NAV_BACK:  // Back button
 | 
						|
      if (CefRefPtr<CefBrowser> browser = GetBrowser()) {
 | 
						|
        browser->GoBack();
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    case IDC_NAV_FORWARD:  // Forward button
 | 
						|
      if (CefRefPtr<CefBrowser> browser = GetBrowser()) {
 | 
						|
        browser->GoForward();
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    case IDC_NAV_RELOAD:  // Reload button
 | 
						|
      if (CefRefPtr<CefBrowser> browser = GetBrowser()) {
 | 
						|
        browser->Reload();
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    case IDC_NAV_STOP:  // Stop button
 | 
						|
      if (CefRefPtr<CefBrowser> browser = GetBrowser()) {
 | 
						|
        browser->StopLoad();
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnFind() {
 | 
						|
  if (find_hwnd_) {
 | 
						|
    // Give focus to the existing find dialog.
 | 
						|
    ::SetFocus(find_hwnd_);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Configure dialog state.
 | 
						|
  ZeroMemory(&find_state_, sizeof(find_state_));
 | 
						|
  find_state_.lStructSize = sizeof(find_state_);
 | 
						|
  find_state_.hwndOwner = hwnd_;
 | 
						|
  find_state_.lpstrFindWhat = find_buff_;
 | 
						|
  find_state_.wFindWhatLen = sizeof(find_buff_);
 | 
						|
  find_state_.Flags = FR_HIDEWHOLEWORD | FR_DOWN;
 | 
						|
 | 
						|
  // Create the dialog.
 | 
						|
  find_hwnd_ = FindText(&find_state_);
 | 
						|
 | 
						|
  // Override the dialog's window procedure.
 | 
						|
  find_wndproc_old_ = SetWndProcPtr(find_hwnd_, FindWndProc);
 | 
						|
 | 
						|
  // Associate |self| with the dialog.
 | 
						|
  SetUserDataPtr(find_hwnd_, this);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnFindEvent() {
 | 
						|
  CefRefPtr<CefBrowser> browser = GetBrowser();
 | 
						|
 | 
						|
  if (find_state_.Flags & FR_DIALOGTERM) {
 | 
						|
    // The find dialog box has been dismissed so invalidate the handle and
 | 
						|
    // reset the search results.
 | 
						|
    if (browser) {
 | 
						|
      browser->GetHost()->StopFinding(true);
 | 
						|
      find_what_last_.clear();
 | 
						|
      find_next_ = false;
 | 
						|
    }
 | 
						|
  } else if ((find_state_.Flags & FR_FINDNEXT) && browser) {
 | 
						|
    // Search for the requested string.
 | 
						|
    bool match_case = ((find_state_.Flags & FR_MATCHCASE) ? true : false);
 | 
						|
    const std::wstring& find_what = find_buff_;
 | 
						|
    if (match_case != find_match_case_last_ || find_what != find_what_last_) {
 | 
						|
      // The search string has changed, so reset the search results.
 | 
						|
      if (!find_what.empty()) {
 | 
						|
        browser->GetHost()->StopFinding(true);
 | 
						|
        find_next_ = false;
 | 
						|
      }
 | 
						|
      find_match_case_last_ = match_case;
 | 
						|
      find_what_last_ = find_buff_;
 | 
						|
    }
 | 
						|
 | 
						|
    browser->GetHost()->Find(find_what,
 | 
						|
                             (find_state_.Flags & FR_DOWN) ? true : false,
 | 
						|
                             match_case, find_next_);
 | 
						|
    if (!find_next_) {
 | 
						|
      find_next_ = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnAbout() {
 | 
						|
  // Show the about box.
 | 
						|
  DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
 | 
						|
            AboutWndProc);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
 | 
						|
  if (IsProcessPerMonitorDpiAware()) {
 | 
						|
    // This call gets Windows to scale the non-client area when WM_DPICHANGED
 | 
						|
    // is fired on Windows versions >= 10.0.14393.0.
 | 
						|
    typedef BOOL(WINAPI * EnableNonClientDpiScalingPtr)(HWND);
 | 
						|
    static EnableNonClientDpiScalingPtr func_ptr =
 | 
						|
        reinterpret_cast<EnableNonClientDpiScalingPtr>(GetProcAddress(
 | 
						|
            GetModuleHandle(L"user32.dll"), "EnableNonClientDpiScaling"));
 | 
						|
    called_enable_non_client_dpi_scaling_ = !!(func_ptr && func_ptr(hwnd_));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnCreate(LPCREATESTRUCT lpCreateStruct) {
 | 
						|
  const HINSTANCE hInstance = lpCreateStruct->hInstance;
 | 
						|
 | 
						|
  RECT rect;
 | 
						|
  GetClientRect(hwnd_, &rect);
 | 
						|
 | 
						|
  if (with_controls_) {
 | 
						|
    // Create the child controls.
 | 
						|
    int x_offset = 0;
 | 
						|
 | 
						|
    const int button_width = GetButtonWidth(hwnd_);
 | 
						|
    const int urlbar_height = GetURLBarHeight(hwnd_);
 | 
						|
 | 
						|
    back_hwnd_ = CreateWindow(
 | 
						|
        L"BUTTON", L"Back", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
 | 
						|
        x_offset, 0, button_width, urlbar_height, hwnd_,
 | 
						|
        reinterpret_cast<HMENU>(IDC_NAV_BACK), hInstance, nullptr);
 | 
						|
    CHECK(back_hwnd_);
 | 
						|
    x_offset += button_width;
 | 
						|
 | 
						|
    forward_hwnd_ = CreateWindow(
 | 
						|
        L"BUTTON", L"Forward",
 | 
						|
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED, x_offset, 0,
 | 
						|
        button_width, urlbar_height, hwnd_,
 | 
						|
        reinterpret_cast<HMENU>(IDC_NAV_FORWARD), hInstance, nullptr);
 | 
						|
    CHECK(forward_hwnd_);
 | 
						|
    x_offset += button_width;
 | 
						|
 | 
						|
    reload_hwnd_ = CreateWindow(
 | 
						|
        L"BUTTON", L"Reload",
 | 
						|
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED, x_offset, 0,
 | 
						|
        button_width, urlbar_height, hwnd_,
 | 
						|
        reinterpret_cast<HMENU>(IDC_NAV_RELOAD), hInstance, nullptr);
 | 
						|
    CHECK(reload_hwnd_);
 | 
						|
    x_offset += button_width;
 | 
						|
 | 
						|
    stop_hwnd_ = CreateWindow(
 | 
						|
        L"BUTTON", L"Stop", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
 | 
						|
        x_offset, 0, button_width, urlbar_height, hwnd_,
 | 
						|
        reinterpret_cast<HMENU>(IDC_NAV_STOP), hInstance, nullptr);
 | 
						|
    CHECK(stop_hwnd_);
 | 
						|
    x_offset += button_width;
 | 
						|
 | 
						|
    edit_hwnd_ =
 | 
						|
        CreateWindow(L"EDIT", nullptr,
 | 
						|
                     WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
 | 
						|
                         ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_DISABLED,
 | 
						|
                     x_offset, 0, rect.right - button_width * 4, urlbar_height,
 | 
						|
                     hwnd_, nullptr, hInstance, nullptr);
 | 
						|
    CHECK(edit_hwnd_);
 | 
						|
 | 
						|
    // Override the edit control's window procedure.
 | 
						|
    edit_wndproc_old_ = SetWndProcPtr(edit_hwnd_, EditWndProc);
 | 
						|
 | 
						|
    // Associate |this| with the edit window.
 | 
						|
    SetUserDataPtr(edit_hwnd_, this);
 | 
						|
 | 
						|
    rect.top += urlbar_height;
 | 
						|
 | 
						|
    if (!with_osr_) {
 | 
						|
      // Remove the menu items that are only used with OSR.
 | 
						|
      HMENU hMenu = ::GetMenu(hwnd_);
 | 
						|
      if (hMenu) {
 | 
						|
        HMENU hTestMenu = ::GetSubMenu(hMenu, 2);
 | 
						|
        if (hTestMenu) {
 | 
						|
          ::RemoveMenu(hTestMenu, ID_TESTS_OSR_FPS, MF_BYCOMMAND);
 | 
						|
          ::RemoveMenu(hTestMenu, ID_TESTS_OSR_DSF, MF_BYCOMMAND);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    // No controls so also remove the default menu.
 | 
						|
    ::SetMenu(hwnd_, nullptr);
 | 
						|
  }
 | 
						|
 | 
						|
  const float device_scale_factor = GetWindowScaleFactor(hwnd_);
 | 
						|
 | 
						|
  if (with_osr_) {
 | 
						|
    browser_window_->SetDeviceScaleFactor(device_scale_factor);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!is_popup_) {
 | 
						|
    // Create the browser window.
 | 
						|
    CefRect cef_rect(rect.left, rect.top, rect.right - rect.left,
 | 
						|
                     rect.bottom - rect.top);
 | 
						|
    browser_window_->CreateBrowser(hwnd_, cef_rect, browser_settings_, nullptr,
 | 
						|
                                   delegate_->GetRequestContext());
 | 
						|
  } else {
 | 
						|
    // With popups we already have a browser window. Parent the browser window
 | 
						|
    // to the root window and show it in the correct location.
 | 
						|
    browser_window_->ShowPopup(hwnd_, rect.left, rect.top,
 | 
						|
                               rect.right - rect.left, rect.bottom - rect.top);
 | 
						|
  }
 | 
						|
 | 
						|
  window_created_ = true;
 | 
						|
}
 | 
						|
 | 
						|
bool RootWindowWin::OnClose() {
 | 
						|
  if (browser_window_ && !browser_window_->IsClosing()) {
 | 
						|
    CefRefPtr<CefBrowser> browser = GetBrowser();
 | 
						|
    if (browser) {
 | 
						|
      // Notify the browser window that we would like to close it. This
 | 
						|
      // will result in a call to ClientHandler::DoClose() if the
 | 
						|
      // JavaScript 'onbeforeunload' event handler allows it.
 | 
						|
      browser->GetHost()->CloseBrowser(false);
 | 
						|
 | 
						|
      // Cancel the close.
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Retrieve current window placement information.
 | 
						|
  WINDOWPLACEMENT placement;
 | 
						|
  ::GetWindowPlacement(hwnd_, &placement);
 | 
						|
 | 
						|
  if (CefCurrentlyOn(TID_UI)) {
 | 
						|
    SaveWindowRestoreOnUIThread(placement);
 | 
						|
  } else {
 | 
						|
    CefPostTask(
 | 
						|
        TID_UI,
 | 
						|
        base::BindOnce(&RootWindowWin::SaveWindowRestoreOnUIThread, placement));
 | 
						|
  }
 | 
						|
 | 
						|
  // Allow the close.
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnDestroyed() {
 | 
						|
  window_destroyed_ = true;
 | 
						|
  NotifyDestroyedIfDone();
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (is_popup_) {
 | 
						|
    // For popup browsers create the root window once the browser has been
 | 
						|
    // created.
 | 
						|
    CreateRootWindow(CefBrowserSettings(), false);
 | 
						|
  } else {
 | 
						|
    // Make sure the browser is sized correctly.
 | 
						|
    OnSize(false);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnBrowserWindowDestroyed() {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  browser_window_.reset();
 | 
						|
 | 
						|
  if (!window_destroyed_) {
 | 
						|
    // The browser was destroyed first. This could be due to the use of
 | 
						|
    // off-screen rendering or native (external) parent, or execution of
 | 
						|
    // JavaScript window.close(). Close the RootWindow asyncronously to allow
 | 
						|
    // the current call stack to unwind.
 | 
						|
    MAIN_POST_CLOSURE(base::BindOnce(&RootWindowWin::Close, this, true));
 | 
						|
  }
 | 
						|
 | 
						|
  browser_destroyed_ = true;
 | 
						|
  NotifyDestroyedIfDone();
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnSetAddress(const std::string& url) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (edit_hwnd_) {
 | 
						|
    SetWindowText(edit_hwnd_, CefString(url).ToWString().c_str());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnSetTitle(const std::string& title) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (hwnd_) {
 | 
						|
    SetWindowText(hwnd_, CefString(title).ToWString().c_str());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnSetFullscreen(bool fullscreen) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  CefRefPtr<CefBrowser> browser = GetBrowser();
 | 
						|
  if (browser) {
 | 
						|
    std::unique_ptr<window_test::WindowTestRunnerWin> test_runner(
 | 
						|
        new window_test::WindowTestRunnerWin());
 | 
						|
    if (fullscreen) {
 | 
						|
      test_runner->Maximize(browser);
 | 
						|
    } else {
 | 
						|
      test_runner->Restore(browser);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnAutoResize(const CefSize& new_size) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (!hwnd_) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  int new_width = new_size.width;
 | 
						|
 | 
						|
  // Make the window wide enough to drag by the top menu bar.
 | 
						|
  if (new_width < 200) {
 | 
						|
    new_width = 200;
 | 
						|
  }
 | 
						|
 | 
						|
  const float device_scale_factor = GetWindowScaleFactor(hwnd_);
 | 
						|
  RECT rect = {0, 0, LogicalToDevice(new_width, device_scale_factor),
 | 
						|
               LogicalToDevice(new_size.height, device_scale_factor)};
 | 
						|
  DWORD style = GetWindowLong(hwnd_, GWL_STYLE);
 | 
						|
  DWORD ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE);
 | 
						|
  bool has_menu = !(style & WS_CHILD) && (GetMenu(hwnd_) != nullptr);
 | 
						|
 | 
						|
  // The size value is for the client area. Calculate the whole window size
 | 
						|
  // based on the current style.
 | 
						|
  AdjustWindowRectEx(&rect, style, has_menu, ex_style);
 | 
						|
 | 
						|
  // Size the window. The left/top values may be negative.
 | 
						|
  // Also show the window if it's not currently visible.
 | 
						|
  SetWindowPos(hwnd_, nullptr, 0, 0, rect.right - rect.left,
 | 
						|
               rect.bottom - rect.top,
 | 
						|
               SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::OnSetLoadingState(bool isLoading,
 | 
						|
                                      bool canGoBack,
 | 
						|
                                      bool canGoForward) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  if (with_controls_) {
 | 
						|
    EnableWindow(back_hwnd_, canGoBack);
 | 
						|
    EnableWindow(forward_hwnd_, canGoForward);
 | 
						|
    EnableWindow(reload_hwnd_, !isLoading);
 | 
						|
    EnableWindow(stop_hwnd_, isLoading);
 | 
						|
    EnableWindow(edit_hwnd_, TRUE);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!isLoading && GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
 | 
						|
    // Done with the initial navigation. Remove the WS_EX_NOACTIVATE style so
 | 
						|
    // that future mouse clicks inside the browser correctly activate and focus
 | 
						|
    // the window. For the top-level window removing this style causes Windows
 | 
						|
    // to display the task bar button.
 | 
						|
    SetWindowLongPtr(hwnd_, GWL_EXSTYLE,
 | 
						|
                     GetWindowLongPtr(hwnd_, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
 | 
						|
 | 
						|
    if (browser_window_) {
 | 
						|
      HWND browser_hwnd = browser_window_->GetWindowHandle();
 | 
						|
      SetWindowLongPtr(
 | 
						|
          browser_hwnd, GWL_EXSTYLE,
 | 
						|
          GetWindowLongPtr(browser_hwnd, GWL_EXSTYLE) & ~WS_EX_NOACTIVATE);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
LPCWSTR kParentWndProc = L"CefParentWndProc";
 | 
						|
LPCWSTR kDraggableRegion = L"CefDraggableRegion";
 | 
						|
 | 
						|
LRESULT CALLBACK SubclassedWindowProc(HWND hWnd,
 | 
						|
                                      UINT message,
 | 
						|
                                      WPARAM wParam,
 | 
						|
                                      LPARAM lParam) {
 | 
						|
  WNDPROC hParentWndProc =
 | 
						|
      reinterpret_cast<WNDPROC>(::GetPropW(hWnd, kParentWndProc));
 | 
						|
  HRGN hRegion = reinterpret_cast<HRGN>(::GetPropW(hWnd, kDraggableRegion));
 | 
						|
 | 
						|
  if (message == WM_NCHITTEST) {
 | 
						|
    LRESULT hit = CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
 | 
						|
    if (hit == HTCLIENT) {
 | 
						|
      POINTS points = MAKEPOINTS(lParam);
 | 
						|
      POINT point = {points.x, points.y};
 | 
						|
      ::ScreenToClient(hWnd, &point);
 | 
						|
      if (::PtInRegion(hRegion, point.x, point.y)) {
 | 
						|
        // Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT
 | 
						|
        // in child windows.
 | 
						|
        return HTTRANSPARENT;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return hit;
 | 
						|
  }
 | 
						|
 | 
						|
  return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
 | 
						|
}
 | 
						|
 | 
						|
void SubclassWindow(HWND hWnd, HRGN hRegion) {
 | 
						|
  HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc);
 | 
						|
  if (hParentWndProc) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  SetLastError(0);
 | 
						|
  LONG_PTR hOldWndProc = SetWindowLongPtr(
 | 
						|
      hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
 | 
						|
  if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  ::SetPropW(hWnd, kParentWndProc, reinterpret_cast<HANDLE>(hOldWndProc));
 | 
						|
  ::SetPropW(hWnd, kDraggableRegion, reinterpret_cast<HANDLE>(hRegion));
 | 
						|
}
 | 
						|
 | 
						|
void UnSubclassWindow(HWND hWnd) {
 | 
						|
  LONG_PTR hParentWndProc =
 | 
						|
      reinterpret_cast<LONG_PTR>(::GetPropW(hWnd, kParentWndProc));
 | 
						|
  if (hParentWndProc) {
 | 
						|
    [[maybe_unused]] LONG_PTR hPreviousWndProc =
 | 
						|
        SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
 | 
						|
    DCHECK_EQ(hPreviousWndProc,
 | 
						|
              reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
 | 
						|
  }
 | 
						|
 | 
						|
  ::RemovePropW(hWnd, kParentWndProc);
 | 
						|
  ::RemovePropW(hWnd, kDraggableRegion);
 | 
						|
}
 | 
						|
 | 
						|
BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) {
 | 
						|
  SubclassWindow(hwnd, reinterpret_cast<HRGN>(lParam));
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) {
 | 
						|
  UnSubclassWindow(hwnd);
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
void RootWindowWin::OnSetDraggableRegions(
 | 
						|
    const std::vector<CefDraggableRegion>& regions) {
 | 
						|
  REQUIRE_MAIN_THREAD();
 | 
						|
 | 
						|
  // Reset draggable region.
 | 
						|
  ::SetRectRgn(draggable_region_, 0, 0, 0, 0);
 | 
						|
 | 
						|
  // Determine new draggable region.
 | 
						|
  std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
 | 
						|
  for (; it != regions.end(); ++it) {
 | 
						|
    HRGN region = ::CreateRectRgn(it->bounds.x, it->bounds.y,
 | 
						|
                                  it->bounds.x + it->bounds.width,
 | 
						|
                                  it->bounds.y + it->bounds.height);
 | 
						|
    ::CombineRgn(draggable_region_, draggable_region_, region,
 | 
						|
                 it->draggable ? RGN_OR : RGN_DIFF);
 | 
						|
    ::DeleteObject(region);
 | 
						|
  }
 | 
						|
 | 
						|
  // Subclass child window procedures in order to do hit-testing.
 | 
						|
  // This will be a no-op, if it is already subclassed.
 | 
						|
  if (hwnd_) {
 | 
						|
    WNDENUMPROC proc =
 | 
						|
        !regions.empty() ? SubclassWindowsProc : UnSubclassWindowsProc;
 | 
						|
    ::EnumChildWindows(hwnd_, proc,
 | 
						|
                       reinterpret_cast<LPARAM>(draggable_region_));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RootWindowWin::NotifyDestroyedIfDone() {
 | 
						|
  // Notify once both the window and the browser have been destroyed.
 | 
						|
  if (window_destroyed_ && browser_destroyed_) {
 | 
						|
    delegate_->OnRootWindowDestroyed(this);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// static
 | 
						|
void RootWindowWin::SaveWindowRestoreOnUIThread(
 | 
						|
    const WINDOWPLACEMENT& placement) {
 | 
						|
  CEF_REQUIRE_UI_THREAD();
 | 
						|
 | 
						|
  cef_show_state_t show_state = CEF_SHOW_STATE_NORMAL;
 | 
						|
  if (placement.showCmd == SW_SHOWMINIMIZED) {
 | 
						|
    show_state = CEF_SHOW_STATE_MINIMIZED;
 | 
						|
  } else if (placement.showCmd == SW_SHOWMAXIMIZED) {
 | 
						|
    show_state = CEF_SHOW_STATE_MAXIMIZED;
 | 
						|
  }
 | 
						|
 | 
						|
  // Coordinates when the window is in the restored position.
 | 
						|
  const auto rect = placement.rcNormalPosition;
 | 
						|
  CefRect pixel_bounds(rect.left, rect.top, rect.right - rect.left,
 | 
						|
                       rect.bottom - rect.top);
 | 
						|
  const auto dip_bounds = CefDisplay::ConvertScreenRectFromPixels(pixel_bounds);
 | 
						|
 | 
						|
  prefs::SaveWindowRestorePreferences(show_state, dip_bounds);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace client
 |