wef 0.6.0

Wef is a Rust library for embedding WebView functionality using Chromium Embedded Framework (CEF3) with offscreen rendering support.
#include "wef.h"

#include <algorithm>
#include <iostream>
#include <vector>

#include "app.h"
#include "app_render_process.h"
#include "browser_callbacks.h"
#include "client.h"
#include "frame.h"
#include "include/base/cef_bind.h"
#include "include/base/cef_callback.h"
#include "include/cef_app.h"
#include "include/cef_command_line.h"
#include "include/cef_render_handler.h"
#include "include/cef_task.h"
#include "include/wrapper/cef_closure_task.h"
#include "shutdown_helper.h"

const uint32_t ALL_MOUSE_BUTTONS = EVENTFLAG_LEFT_MOUSE_BUTTON;

struct WefSettings {
  const char* locale;
  const char* cache_path;
  const char* root_cache_path;
  const char* browser_subprocess_path;
};

struct WefBrowserSettings {
  void* parent;
  float device_scale_factor;
  int width;
  int height;
  int frame_rate;
  const char* url;
  const char* inject_javascript;
  BrowserCallbacks callbacks;
  void* userdata;
  DestroyFn destroy_userdata;
};

inline void apply_key_modifiers(uint32_t& m, int modifiers) {
  if (modifiers & 0x1) {
    m |= EVENTFLAG_SHIFT_DOWN;
  }

  if (modifiers & 0x2) {
    m |= EVENTFLAG_CONTROL_DOWN;
  }

  if (modifiers & 0x4) {
    m |= EVENTFLAG_ALT_DOWN;
  }
}

extern "C" {

bool wef_init(const WefSettings* wef_settings) {
  CefSettings settings;
  settings.windowless_rendering_enabled = true;

#ifdef __APPLE__
  settings.no_sandbox = false;
#else
  settings.no_sandbox = true;
#endif

  settings.external_message_pump = true;

  if (wef_settings->locale) {
    CefString(&settings.locale) = wef_settings->locale;
  }

  if (wef_settings->cache_path) {
    CefString(&settings.cache_path) = wef_settings->cache_path;
  }

  if (wef_settings->root_cache_path) {
    CefString(&settings.root_cache_path) = wef_settings->root_cache_path;
  }

  if (wef_settings->browser_subprocess_path) {
    CefString(&settings.browser_subprocess_path) =
        wef_settings->browser_subprocess_path;
  }

  CefRefPtr<WefApp> app(new WefApp());
  return CefInitialize(CefMainArgs(), settings, app, nullptr);
}

bool wef_exec_process(char* argv[], int argc) {
#ifdef WIN32
  CefMainArgs args(GetModuleHandle(NULL));
#else
  CefMainArgs args(argc, argv);
#endif

  CefRefPtr<WefRenderProcessApp> app(new WefRenderProcessApp());
  return CefExecuteProcess(args, app, nullptr) >= 0;
}

void wef_shutdown() {
  ShutdownHelper::getSingleton()->shutdown();
  CefShutdown();
}

WefBrowser* wef_browser_create(const WefBrowserSettings* settings) {
  CefWindowInfo window_info;
  window_info.SetAsWindowless(
      reinterpret_cast<CefWindowHandle>(settings->parent));
  window_info.runtime_style = CEF_RUNTIME_STYLE_ALLOY;

  CefBrowserSettings browser_settings;
  browser_settings.windowless_frame_rate = settings->frame_rate;
  browser_settings.background_color = CefColorSetARGB(255, 255, 255, 255);

  WefBrowser* wef_browser = new WefBrowser;
  CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
  extra_info->SetString("__wef_inject_javascript", settings->inject_javascript);

  CefRefPtr<WefClient> client(
      new WefClient(wef_browser, settings->device_scale_factor, settings->width,
                    settings->height, settings->callbacks, settings->userdata,
                    settings->destroy_userdata));
  CefBrowserHost::CreateBrowser(window_info, client, "about:blank",
                                browser_settings, extra_info, nullptr);

  wef_browser->url = settings->url;
  wef_browser->focus = false;
  wef_browser->client = client;
  wef_browser->browser = std::nullopt;
  wef_browser->cursorX = 0;
  wef_browser->cursorY = 0;

  ShutdownHelper::getSingleton()->browserCreated();
  return wef_browser;
}

void wef_browser_destroy(WefBrowser* browser) {
  if (browser->browser) {
    (*browser->browser)->GetHost()->CloseBrowser(false);
  } else {
    browser->deleteBrowser = true;
  }
}

bool wef_browser_is_created(WefBrowser* browser) {
  return browser->browser.has_value();
}

void wef_browser_set_size(WefBrowser* browser, int width, int height) {
  if (browser->client->setSize(width, height)) {
    if (browser->browser) {
      (*browser->browser)->GetHost()->WasResized();
    }
  }
}

void wef_browser_load_url(WefBrowser* browser, const char* url) {
  if (strlen(url) > 0) {
    if (!browser->browser) {
      browser->url = url;
      return;
    }

    CefString url_str = url;
    CefPostTask(TID_UI,
                base::BindOnce(&CefFrame::LoadURL,
                               (*browser->browser)->GetMainFrame(), url_str));
  }
}

bool wef_browser_can_go_forward(WefBrowser* browser) {
  if (!browser->browser) {
    return false;
  }
  return (*browser->browser)->CanGoForward();
}

bool wef_browser_can_go_back(WefBrowser* browser) {
  if (!browser->browser) {
    return false;
  }
  return (*browser->browser)->CanGoBack();
}

void wef_browser_go_forward(WefBrowser* browser) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)->GoForward();
}

void wef_browser_go_back(WefBrowser* browser) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)->GoBack();
}

void wef_browser_reload(WefBrowser* browser) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)->Reload();
}

void wef_browser_reload_ignore_cache(WefBrowser* browser) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)->ReloadIgnoreCache();
}

void wef_browser_send_mouse_click_event(WefBrowser* browser,
                                        int mouse_button_type, bool mouse_up,
                                        int click_count, int modifiers) {
  if (!browser->browser) {
    return;
  }

  CefMouseEvent mouse_event;
  mouse_event.x = browser->cursorX;
  mouse_event.y = browser->cursorY;
  mouse_event.modifiers = EVENTFLAG_NONE;

  CefBrowserHost::MouseButtonType btn_type;
  switch (mouse_button_type) {
    case 1:
      btn_type = MBT_MIDDLE;
      mouse_event.modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
      break;
    case 2:
      btn_type = MBT_RIGHT;
      mouse_event.modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
      break;
    default:
      btn_type = MBT_LEFT;
      mouse_event.modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
  }

  apply_key_modifiers(mouse_event.modifiers, modifiers);
  (*browser->browser)
      ->GetHost()
      ->SendMouseClickEvent(mouse_event, btn_type, mouse_up,
                            std::min(click_count, 3));
}

void wef_browser_send_mouse_move_event(WefBrowser* browser, int x, int y,
                                       int modifiers) {
  if (!browser->browser) {
    return;
  }

  CefMouseEvent mouse_event;
  mouse_event.x = x;
  mouse_event.y = y;
  mouse_event.modifiers = ALL_MOUSE_BUTTONS;
  apply_key_modifiers(mouse_event.modifiers, modifiers);
  (*browser->browser)->GetHost()->SendMouseMoveEvent(mouse_event, false);

  browser->cursorX = mouse_event.x;
  browser->cursorY = mouse_event.y;
}

void wef_browser_send_mouse_wheel_event(WefBrowser* browser, int delta_x,
                                        int delta_y) {
  if (!browser->browser) {
    return;
  }

  CefMouseEvent mouse_event;
  mouse_event.x = browser->cursorX;
  mouse_event.y = browser->cursorY;
  mouse_event.modifiers = ALL_MOUSE_BUTTONS;
  (*browser->browser)
      ->GetHost()
      ->SendMouseWheelEvent(mouse_event, delta_x, delta_y);
}

void wef_browser_send_key_event(WefBrowser* browser, bool is_down, int key_code,
                                int modifiers) {
  if (!browser->browser) {
    return;
  }

  CefKeyEvent key_event;
  key_event.type = is_down ? KEYEVENT_KEYDOWN : KEYEVENT_KEYUP;
  key_event.modifiers = EVENTFLAG_NONE;
  key_event.focus_on_editable_field = false;
  key_event.is_system_key = false;
  key_event.windows_key_code = key_code;
  key_event.native_key_code = key_code;
  key_event.modifiers = 0;
  apply_key_modifiers(key_event.modifiers, modifiers);
  (*browser->browser)->GetHost()->SendKeyEvent(key_event);
}

void wef_browser_send_char_event(WefBrowser* browser, char16_t ch) {
  if (!browser->browser) {
    return;
  }

  CefKeyEvent key_event;
  key_event.type = KEYEVENT_CHAR;
  key_event.modifiers = EVENTFLAG_NONE;
  key_event.windows_key_code = static_cast<int>(ch);
  key_event.native_key_code = static_cast<int>(ch);
  key_event.character = static_cast<char16_t>(ch);
  (*browser->browser)->GetHost()->SendKeyEvent(key_event);
}

void wef_browser_ime_set_composition(WefBrowser* browser, const char* text,
                                     uint32_t cursor_begin,
                                     uint32_t cursor_end) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)
      ->GetHost()
      ->ImeSetComposition(text, {}, CefRange::InvalidRange(),
                          CefRange(cursor_begin, cursor_end));
}

void wef_browser_ime_commit(WefBrowser* browser, const char* text) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)
      ->GetHost()
      ->ImeCommitText(text, CefRange::InvalidRange(), 0);
}

WefFrame* wef_browser_get_main_frame(WefBrowser* browser) {
  if (!browser->browser) {
    return nullptr;
  }
  auto main_frame = (*browser->browser)->GetMainFrame();
  return main_frame ? new WefFrame{main_frame} : nullptr;
}

WefFrame* wef_browser_get_focused_frame(WefBrowser* browser) {
  if (!browser->browser) {
    return nullptr;
  }
  auto frame = (*browser->browser)->GetFocusedFrame();
  return frame ? new WefFrame{frame} : nullptr;
}

WefFrame* wef_browser_get_frame_by_name(WefBrowser* browser, const char* name) {
  if (!browser->browser) {
    return nullptr;
  }
  auto frame = (*browser->browser)->GetFrameByName(name);
  return frame ? new WefFrame{frame} : nullptr;
}

WefFrame* wef_browser_get_frame_by_identifier(WefBrowser* browser,
                                              const char* id) {
  if (!browser->browser) {
    return nullptr;
  }
  auto frame = (*browser->browser)->GetFrameByIdentifier(id);
  return frame ? new WefFrame{frame} : nullptr;
}

bool wef_browser_is_audio_muted(WefBrowser* browser, bool mute) {
  if (browser->browser) {
    return false;
  }
  return (*browser->browser)->GetHost()->IsAudioMuted();
}

void wef_browser_set_audio_mute(WefBrowser* browser, bool mute) {
  if (browser->browser) {
    return;
  }
  (*browser->browser)->GetHost()->SetAudioMuted(mute);
}

void wef_browser_find(WefBrowser* browser, const char* search_text,
                      bool forward, bool match_case, bool find_next) {
  if (!browser->browser) {
    return;
  }
  (*browser->browser)
      ->GetHost()
      ->Find(search_text, forward, match_case, find_next);
}

void wef_browser_set_focus(WefBrowser* browser, bool focus) {
  if (!browser->browser) {
    browser->focus = true;
    return;
  }
  (*browser->browser)->GetHost()->SetFocus(focus);
}

}  // extern "C"