MAC Resize window via Javascript binding

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

MAC Resize window via Javascript binding

Postby inplainenglish » Thu Jan 16, 2020 2:49 am

Hello all! I am trying to implement a 'window.resizeTo' binding within cefsimple. I am using a 6 month old iMac which is running Mojave 10.14.6.

I have successfully implemented a window binding as described by herein: https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration.

All that remains, really, is to instruct cocoa to actually change the size of the window. It's here that I get what appears to be a very strange error. I say error because the window seems to be frozen at this point. What actually happens is that upon calling the function to change the window size, the window un-paints itself and turns grey.

I'm not seeing any similar errors upon a google search which leads me to think that I'm probably screwing up the syntax.

Two things occur to me.

Firstly, I wonder if I'm trying to call "[window setFrame: newRect display: YES]" in the wrong process. If that is the case, which process do I need to be calling it in, and which process am I currently calling from? Eventually learning IPC will have to happen but I'm still at the start of my cef journey.

Secondly- that the screen turns grey leads to think that maybe an un-paint function is being called prior to the browser then attempting to resize the window. A error might then be occurring which prevents the resize and as such, no re-paint function is being called, resulting in an unpainted, blank page.

Below is all of my code which is in any way different from what ships with the binary. The files window_controller.h, .cc, and .mm are all specific to my project.

CMakeLists.txt
Code: Select all
# Copyright (c) 2014 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.

#
# Source files.
#

# cefsimple sources.
set(CEFSIMPLE_SRCS
  simple_app.cc
  simple_app.h
  simple_handler.cc
  simple_handler.h
  )
set(CEFSIMPLE_SRCS_LINUX
  cefsimple_linux.cc
  simple_handler_linux.cc
  )
set(CEFSIMPLE_SRCS_MACOSX
  cefsimple_mac.mm
  simple_handler_mac.mm
  )
set(CEFSIMPLE_SRCS_WINDOWS
  cefsimple.rc
  cefsimple_win.cc
  resource.h
  simple_handler_win.cc
  )
APPEND_PLATFORM_SOURCES(CEFSIMPLE_SRCS)
source_group(cefsimple FILES ${CEFSIMPLE_SRCS})

set(CEFSIMPLE_SRCS
  ${CEFSIMPLE_SRCS}
  )

# cefsimple helper sources.
set(CEFSIMPLE_HELPER_SRCS_MACOSX
  process_helper_mac.cc
  window_controller.cc
  window_controller.h
  window_controller.mm
  )
APPEND_PLATFORM_SOURCES(CEFSIMPLE_HELPER_SRCS)
source_group(cefsimple FILES ${CEFSIMPLE_HELPER_SRCS})

# cefsimple resources.
set(CEFSIMPLE_RESOURCES_MAC_SRCS_MACOSX
  mac/Info.plist
  mac/cefsimple.icns
  )
APPEND_PLATFORM_SOURCES(CEFSIMPLE_RESOURCES_MAC_SRCS)
source_group(cefsimple\\\\mac FILES ${CEFSIMPLE_RESOURCES_MAC_SRCS})

set(CEFSIMPLE_RESOURCES_MAC_ENGLISH_LPROJ_SRCS_MACOSX
  mac/English.lproj/InfoPlist.strings
  mac/English.lproj/MainMenu.xib
  )
APPEND_PLATFORM_SOURCES(CEFSIMPLE_RESOURCES_MAC_ENGLISH_LPROJ_SRCS)
source_group(cefsimple\\\\mac\\\\English.lproj FILES ${CEFSIMPLE_RESOURCES_MAC_ENGLISH_LPROJ_SRCS})

set(CEFSIMPLE_RESOURCES_SRCS
  ${CEFSIMPLE_RESOURCES_MAC_SRCS}
  ${CEFSIMPLE_RESOURCES_MAC_ENGLISH_LPROJ_SRCS}
  )


#
# Shared configuration.
#

# Target executable names.
set(CEF_TARGET "cefsimple")
if(OS_MACOSX)
  set(CEF_HELPER_TARGET "cefsimple_Helper")
  set(CEF_HELPER_OUTPUT_NAME "cefsimple Helper")
else()
  # Logical target used to link the libcef library.
  ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
endif()

# Determine the target output directory.
SET_CEF_TARGET_OUT_DIR()


#
# Linux configuration.
#

if(OS_LINUX)
  # Executable target.
  add_executable(${CEF_TARGET} ${CEFSIMPLE_SRCS})
  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})

  # Set rpath so that libraries can be placed next to the executable.
  set_target_properties(${CEF_TARGET} PROPERTIES INSTALL_RPATH "$ORIGIN")
  set_target_properties(${CEF_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
  set_target_properties(${CEF_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR})

  # Copy binary and resource files to the target output directory.
  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
  if (EXISTS "${CEF_BINARY_DIR}/libminigbm.so")
    COPY_FILES("${CEF_TARGET}" "libminigbm.so" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
  endif()

  # Set SUID permissions on the chrome-sandbox target.
  SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox")
endif()


#
# Mac OS X configuration.
#

if(OS_MACOSX)
  option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON)
  if(OPTION_USE_ARC)
    list(APPEND CEF_COMPILER_FLAGS
      -fobjc-arc
      )
    set_target_properties(${target} PROPERTIES
      CLANG_ENABLE_OBJC_ARC "YES"
      )
  endif()

  # Output path for the main app bundle.
  set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app")

  # Variables referenced from the main Info.plist file.
  set(EXECUTABLE_NAME "${CEF_TARGET}")
  set(PRODUCT_NAME "${CEF_TARGET}")

  if(USE_SANDBOX)
    # Logical target used to link the cef_sandbox library.
    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
  endif()

  # Main app bundle target.
  add_executable(${CEF_TARGET} MACOSX_BUNDLE ${CEFSIMPLE_RESOURCES_SRCS} ${CEFSIMPLE_SRCS})
  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
  target_link_libraries(${CEF_TARGET} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
  set_target_properties(${CEF_TARGET} PROPERTIES
    MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist
    )

  # Copy the CEF framework into the Frameworks directory.
  add_custom_command(
    TARGET ${CEF_TARGET}
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy_directory
            "${CEF_BINARY_DIR}/Chromium Embedded Framework.framework"
            "${CEF_APP}/Contents/Frameworks/Chromium Embedded Framework.framework"
    VERBATIM
    )

  # Create the multiple Helper app bundle targets.
  foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES})
    # Convert to a list and extract the suffix values.
    string(REPLACE ":" ";" _suffix_list ${_suffix_list})
    list(GET _suffix_list 0 _name_suffix)
    list(GET _suffix_list 1 _target_suffix)
    list(GET _suffix_list 2 _plist_suffix)

    # Define Helper target and output names.
    set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}")
    set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}")

    # Create Helper-specific variants of the helper-Info.plist file. Do this
    # manually because the configure_file command (which is executed as part of
    # MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the
    # wrong values with multiple targets.
    set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist")
    file(READ "${CMAKE_CURRENT_SOURCE_DIR}/mac/helper-Info.plist" _plist_contents)
    string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
    string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
    string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents})
    file(WRITE ${_helper_info_plist} ${_plist_contents})

    # Create Helper executable target.
    add_executable(${_helper_target} MACOSX_BUNDLE ${CEFSIMPLE_HELPER_SRCS})
    SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target})
    add_dependencies(${_helper_target} libcef_dll_wrapper)
    target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
    set_target_properties(${_helper_target} PROPERTIES
      MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist}
      OUTPUT_NAME ${_helper_output_name}
      )

    if(USE_SANDBOX)
      target_link_libraries(${_helper_target} cef_sandbox_lib)
    endif()

    # Add the Helper as a dependency of the main executable target.
    add_dependencies(${CEF_TARGET} "${_helper_target}")

    # Copy the Helper app bundle into the Frameworks directory.
    add_custom_command(
      TARGET ${CEF_TARGET}
      POST_BUILD
      COMMAND ${CMAKE_COMMAND} -E copy_directory
              "${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app"
              "${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app"
      VERBATIM
      )
  endforeach()

  # Manually process and copy over resource files.
  # The Xcode generator can support this via the set_target_properties RESOURCE
  # directive but that doesn't properly handle nested resource directories.
  # Remove these prefixes from input file paths.
  set(PREFIXES "mac/")
  COPY_MACOSX_RESOURCES("${CEFSIMPLE_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()


#
# Windows configuration.
#

if(OS_WINDOWS)
  # Executable target.
  add_executable(${CEF_TARGET} WIN32 ${CEFSIMPLE_SRCS})
  add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
  SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
  target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})

  if(USE_SANDBOX)
    # Logical target used to link the cef_sandbox library.
    ADD_LOGICAL_TARGET("cef_sandbox_lib" "${CEF_SANDBOX_LIB_DEBUG}" "${CEF_SANDBOX_LIB_RELEASE}")
    target_link_libraries(${CEF_TARGET} cef_sandbox_lib ${CEF_SANDBOX_STANDARD_LIBS})
  endif()

  # Add the custom manifest files to the executable.
  ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_TARGET}" "exe")

  # Copy binary and resource files to the target output directory.
  COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
  COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
endif()


process_helper_mac.mm
Code: Select all
// Copyright (c) 2013 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 "include/cef_app.h"
#include "include/wrapper/cef_library_loader.h"

#include "tests/cefsimple/window_controller.h"

// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
// use of the sandbox.
#if defined(CEF_USE_SANDBOX)
#include "include/cef_sandbox_mac.h"
#endif

// Entry point function for sub-processes.
int main(int argc, char* argv[]) {
#if defined(CEF_USE_SANDBOX)
  // Initialize the macOS sandbox for this helper process.
  CefScopedSandboxContext sandbox_context;
  if (!sandbox_context.Initialize(argc, argv))
    return 1;
#endif

  // Load the CEF framework library at runtime instead of linking directly
  // as required by the macOS sandbox implementation.
  CefScopedLibraryLoader library_loader;
  if (!library_loader.LoadInHelper())
    return 1;

  // Provide CEF with command-line arguments.
  CefMainArgs main_args(argc, argv);

  CefRefPtr<WindowController> app(new WindowController);

  // Execute the sub-process.
  return CefExecuteProcess(main_args, app, NULL);
}


window_controller.h
Code: Select all
// Copyright (c) 2013 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.

#ifndef CEF_TESTS_CEFSIMPLE_WINDOW_CONTROLLER_H_
#define CEF_TESTS_CEFSIMPLE_WINDOW_CONTROLLER_H_

#include "include/cef_app.h"
#include "include/cef_browser.h"

// Implement application-level callbacks for the browser process.
class WindowController : public CefApp, public CefRenderProcessHandler {
 public:
  WindowController();

  static void SetPos(CefRefPtr<CefBrowser> browser,
                      int x,
                      int y,
                      int width,
                      int height);

  static void Minimize(CefRefPtr<CefBrowser> browser);
  static void Maximize(CefRefPtr<CefBrowser> browser);
  static void Restore(CefRefPtr<CefBrowser> browser);

  static void ModifyBounds(const CefRect& display, CefRect& window);

  // CefApp methods:
  virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler()
      OVERRIDE {
    return this;

  }

  virtual void OnContextCreated( CefRefPtr<CefBrowser> browser,
                                CefRefPtr<CefFrame> frame,
                                CefRefPtr<CefV8Context> context ) OVERRIDE;
 

 private:

  IMPLEMENT_REFCOUNTING(WindowController);

};

#endif  // CEF_TESTS_CEFSIMPLE_WINDOW_CONTROLLER_H_


window_controller.cc
Code: Select all
// Copyright (c) 2013 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/cefsimple/window_controller.h"

class resizeToHandler : public CefV8Handler {
public:

  resizeToHandler() {}

  virtual bool Execute(const CefString& name,
                       CefRefPtr<CefV8Value> object,
                       const CefV8ValueList& arguments,
                       CefRefPtr<CefV8Value>& retval,
                       CefString& exception) OVERRIDE {

    retval = NULL;

    if (name == "_resizeTo") {

      CefRefPtr<CefBrowser> browser = CefV8Context::GetCurrentContext()->GetBrowser();

      int xPos = 100;
      int yPos = 100;

      int newWidth = arguments[0]->GetIntValue();
      int newHeight = arguments[1]->GetIntValue();

      WindowController::SetPos( browser,
                                xPos,
                                yPos,
                                newWidth,
                                newHeight );
     
      return true;

    }

    return false;
   
  }

  // Provide the reference counting implementation for this class.
  IMPLEMENT_REFCOUNTING(resizeToHandler);
};

WindowController::WindowController() {}

void WindowController::OnContextCreated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefV8Context> context) {
 
  CefRefPtr<CefV8Value> object = context->GetGlobal();

  CefRefPtr<CefV8Handler> _resizeToHandler = new resizeToHandler();

  CefRefPtr<CefV8Value> _resizeToFunction = CefV8Value::CreateFunction("_resizeTo", _resizeToHandler);

  object->SetValue("_resizeTo", _resizeToFunction, V8_PROPERTY_ATTRIBUTE_NONE);

}

void WindowController::ModifyBounds(const CefRect& display, CefRect& window) {
  window.x += display.x;
  window.y += display.y;

  if (window.x < display.x)
    window.x = display.x;
  if (window.y < display.y)
    window.y = display.y;
  if (window.width < 100)
    window.width = 100;
  else if (window.width >= display.width)
    window.width = display.width;
  if (window.height < 100)
    window.height = 100;
  else if (window.height >= display.height)
    window.height = display.height;
  if (window.x + window.width >= display.x + display.width)
    window.x = display.x + display.width - window.width;
  if (window.y + window.height >= display.y + display.height)
    window.y = display.y + display.height - window.height;
}


window_controller.mm
Code: Select all
// Copyright (c) 2013 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/cefsimple/window_controller.h"

#import <Cocoa/Cocoa.h>

#include "include/cef_browser.h"

NSWindow* GetWindow(CefRefPtr<CefBrowser> browser) {
  NSView* view =
      CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetHost()->GetWindowHandle());
  return [view window];
}

void WindowController::SetPos(CefRefPtr<CefBrowser> browser,
                                 int xPos,
                                 int yPos,
                                 int width,
                                 int height ) {

  NSWindow* window = GetWindow(browser);

  NSRect newRect;
  newRect.origin.x = xPos;
  newRect.origin.y = yPos;
  newRect.size.width = width;
  newRect.size.height = height;
  [window setFrame: newRect display: YES];

}

void WindowController::Minimize(CefRefPtr<CefBrowser> browser) {

}

void WindowController::Maximize(CefRefPtr<CefBrowser> browser) {

}

void WindowController::Restore(CefRefPtr<CefBrowser> browser) {

}


resize_test.html
Code: Select all
<!DOCTYPE html>
<html>
<head>
   <title></title>
</head>
<body style="background-color: black">

   <script type="text/javascript">

      window.resizeTo( 500, 250 );
   
   </script>

</body>
</html>


Sorry I can't give a more detailed description of the problem. I'm a bit out of my depth. Hopefully my code will speak for themselves.
inplainenglish
Newbie
 
Posts: 6
Joined: Thu Oct 24, 2019 12:50 pm

Re: MAC Resize window via Javascript binding

Postby magreenblatt » Thu Jan 16, 2020 3:42 am

GetHost() will return null in the renderer process, so your code is crashing. You need to resize the window in the main process. You can implement that using the async JS bindings described here.
magreenblatt
Site Admin
 
Posts: 12407
Joined: Fri May 29, 2009 6:57 pm

Re: MAC Resize window via Javascript binding

Postby inplainenglish » Fri Jan 17, 2020 12:20 am

I see. I had a feeling it was something like that.

So, while I'm going about figuring IPC and such I have a follow up question.

I see that in this (https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=16407&p=40227&hilit=GetCurrentContext+%3EGetBrowser+#p40227) thread there is a reference to a different method for retrieving the browser- one which seems to be executed from within a V8Handler and therefore from within a render-process.

Code: Select all
CefRefPtr<CefBrowser> browser = CefV8Context::GetCurrentContext()->GetBrowser();


Is it that ANY attempt to retrieve the browser will result in NULL from within a render-process? I attempted the above in my own code and had no success so I would guess as much. The term 'browser-process' leads me to think that the browser-object would only be accessable therein.

But more specifically I would really appreciate this clarification: Does ANY attempt to reference the window from the cocoa/c++ side of things HAVE to occur in the browser process? I've heard of c++ calls in the render process happening on occasion but generally being frowned upon. I just don't want to go on a wild goose chase if I can help it.

I guess my question is this:

In order to implement resizing the window, I need to pass a request from my V8Handler in the render process to the browser process via a message, and then perform a call to cocoa's actual resize method from there.

Correct?
inplainenglish
Newbie
 
Posts: 6
Joined: Thu Oct 24, 2019 12:50 pm

Re: MAC Resize window via Javascript binding

Postby magreenblatt » Fri Jan 17, 2020 3:19 am

In order to implement resizing the window, I need to pass a request from my V8Handler in the render process to the browser process via a message, and then perform a call to cocoa's actual resize method from there.

Correct?

Yes. CefBrowser exists in both processes. CefBrowserHost only exists in the main process.
magreenblatt
Site Admin
 
Posts: 12407
Joined: Fri May 29, 2009 6:57 pm


Return to Support Forum

Who is online

Users browsing this forum: No registered users and 24 guests