feat: replace BrowserView with WebContentsView (#35658)

This commit is contained in:
Jeremy Rose 2023-12-13 13:01:03 -08:00 committed by GitHub
parent a94fb2cb5d
commit 15c6014324
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 2987 additions and 1531 deletions

View file

@ -13,7 +13,6 @@
#include "base/task/single_thread_task_runner.h"
#include "electron/buildflags/buildflags.h"
#include "gin/dictionary.h"
#include "shell/browser/api/electron_api_browser_view.h"
#include "shell/browser/api/electron_api_menu.h"
#include "shell/browser/api/electron_api_view.h"
#include "shell/browser/api/electron_api_web_contents.h"
@ -97,6 +96,8 @@ BaseWindow::BaseWindow(v8::Isolate* isolate,
options, parent.IsEmpty() ? nullptr : parent->window_.get()));
window_->AddObserver(this);
SetContentView(View::Create(isolate));
#if defined(TOOLKIT_VIEWS)
v8::Local<v8::Value> icon;
if (options.Get(options::kIcon, &icon)) {
@ -164,7 +165,6 @@ void BaseWindow::OnWindowClosed() {
Emit("closed");
RemoveFromParentChildWindows();
BaseWindow::ResetBrowserViews();
// Destroy the native class when window is closed.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
@ -311,8 +311,7 @@ void BaseWindow::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {
#endif
void BaseWindow::SetContentView(gin::Handle<View> view) {
ResetBrowserViews();
content_view_.Reset(isolate(), view.ToV8());
content_view_.Reset(JavascriptEnvironment::GetIsolate(), view.ToV8());
window_->SetContentView(view->view());
}
@ -749,70 +748,6 @@ void BaseWindow::SetParentWindow(v8::Local<v8::Value> value,
}
}
void BaseWindow::SetBrowserView(
absl::optional<gin::Handle<BrowserView>> browser_view) {
ResetBrowserViews();
if (browser_view)
AddBrowserView(*browser_view);
}
void BaseWindow::AddBrowserView(gin::Handle<BrowserView> browser_view) {
if (!base::Contains(browser_views_, browser_view.ToV8())) {
// If we're reparenting a BrowserView, ensure that it's detached from
// its previous owner window.
BaseWindow* owner_window = browser_view->owner_window();
if (owner_window) {
// iter == browser_views_.end() should imply owner_window != this.
DCHECK_NE(owner_window, this);
owner_window->RemoveBrowserView(browser_view);
browser_view->SetOwnerWindow(nullptr);
}
// If the user set the BrowserView's bounds before adding it to the window,
// we need to get those initial bounds *before* adding it to the window
// so bounds isn't returned relative despite not being correctly positioned
// relative to the window.
auto bounds = browser_view->GetBounds();
window_->AddBrowserView(browser_view->view());
window_->AddDraggableRegionProvider(browser_view.get());
browser_view->SetOwnerWindow(this);
browser_views_.emplace_back().Reset(isolate(), browser_view.ToV8());
// Recalibrate bounds relative to the containing window.
if (!bounds.IsEmpty())
browser_view->SetBounds(bounds);
}
}
void BaseWindow::RemoveBrowserView(gin::Handle<BrowserView> browser_view) {
auto iter = std::find(browser_views_.begin(), browser_views_.end(),
browser_view.ToV8());
if (iter != browser_views_.end()) {
window_->RemoveDraggableRegionProvider(browser_view.get());
window_->RemoveBrowserView(browser_view->view());
browser_view->SetOwnerWindow(nullptr);
iter->Reset();
browser_views_.erase(iter);
}
}
void BaseWindow::SetTopBrowserView(gin::Handle<BrowserView> browser_view,
gin_helper::Arguments* args) {
BaseWindow* owner_window = browser_view->owner_window();
auto iter = std::find(browser_views_.begin(), browser_views_.end(),
browser_view.ToV8());
if (iter == browser_views_.end() || (owner_window && owner_window != this)) {
args->ThrowError("Given BrowserView is not attached to the window");
return;
}
browser_views_.erase(iter);
browser_views_.emplace_back().Reset(isolate(), browser_view.ToV8());
window_->SetTopBrowserView(browser_view->view());
}
std::string BaseWindow::GetMediaSourceId() const {
return window_->GetDesktopMediaID().ToString();
}
@ -1018,31 +953,6 @@ std::vector<v8::Local<v8::Object>> BaseWindow::GetChildWindows() const {
return child_windows_.Values(isolate());
}
v8::Local<v8::Value> BaseWindow::GetBrowserView(
gin_helper::Arguments* args) const {
if (browser_views_.empty()) {
return v8::Null(isolate());
} else if (browser_views_.size() == 1) {
auto first_view = browser_views_.begin();
return v8::Local<v8::Value>::New(isolate(), *first_view);
} else {
args->ThrowError(
"BrowserWindow have multiple BrowserViews, "
"Use getBrowserViews() instead");
return v8::Null(isolate());
}
}
std::vector<v8::Local<v8::Value>> BaseWindow::GetBrowserViews() const {
std::vector<v8::Local<v8::Value>> ret;
for (auto const& browser_view : browser_views_) {
ret.push_back(v8::Local<v8::Value>::New(isolate(), browser_view));
}
return ret;
}
bool BaseWindow::IsModal() const {
return window_->is_modal();
}
@ -1140,31 +1050,6 @@ int32_t BaseWindow::GetID() const {
return weak_map_id();
}
void BaseWindow::ResetBrowserViews() {
v8::HandleScope scope(isolate());
for (auto& item : browser_views_) {
gin::Handle<BrowserView> browser_view;
if (gin::ConvertFromV8(isolate(),
v8::Local<v8::Value>::New(isolate(), item),
&browser_view) &&
!browser_view.IsEmpty()) {
// There's a chance that the BrowserView may have been reparented - only
// reset if the owner window is *this* window.
BaseWindow* owner_window = browser_view->owner_window();
DCHECK_EQ(owner_window, this);
browser_view->SetOwnerWindow(nullptr);
window_->RemoveBrowserView(browser_view->view());
window_->RemoveDraggableRegionProvider(browser_view.get());
browser_view->SetOwnerWindow(nullptr);
}
item.Reset();
}
browser_views_.clear();
}
void BaseWindow::RemoveFromParentChildWindows() {
if (parent_window_.IsEmpty())
return;
@ -1274,10 +1159,6 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setMenu", &BaseWindow::SetMenu)
.SetMethod("removeMenu", &BaseWindow::RemoveMenu)
.SetMethod("setParentWindow", &BaseWindow::SetParentWindow)
.SetMethod("setBrowserView", &BaseWindow::SetBrowserView)
.SetMethod("addBrowserView", &BaseWindow::AddBrowserView)
.SetMethod("removeBrowserView", &BaseWindow::RemoveBrowserView)
.SetMethod("setTopBrowserView", &BaseWindow::SetTopBrowserView)
.SetMethod("getMediaSourceId", &BaseWindow::GetMediaSourceId)
.SetMethod("getNativeWindowHandle", &BaseWindow::GetNativeWindowHandle)
.SetMethod("setProgressBar", &BaseWindow::SetProgressBar)
@ -1333,10 +1214,10 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
.SetMethod("previewFile", &BaseWindow::PreviewFile)
.SetMethod("closeFilePreview", &BaseWindow::CloseFilePreview)
.SetMethod("getContentView", &BaseWindow::GetContentView)
.SetProperty("contentView", &BaseWindow::GetContentView,
&BaseWindow::SetContentView)
.SetMethod("getParentWindow", &BaseWindow::GetParentWindow)
.SetMethod("getChildWindows", &BaseWindow::GetChildWindows)
.SetMethod("getBrowserView", &BaseWindow::GetBrowserView)
.SetMethod("getBrowserViews", &BaseWindow::GetBrowserViews)
.SetMethod("isModal", &BaseWindow::IsModal)
.SetMethod("setThumbarButtons", &BaseWindow::SetThumbarButtons)
#if defined(TOOLKIT_VIEWS)

View file

@ -22,7 +22,6 @@
namespace electron::api {
class View;
class BrowserView;
class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
public NativeWindowObserver {
@ -173,14 +172,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
void SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> menu);
void RemoveMenu();
void SetParentWindow(v8::Local<v8::Value> value, gin_helper::Arguments* args);
virtual void SetBrowserView(
absl::optional<gin::Handle<BrowserView>> browser_view);
virtual void AddBrowserView(gin::Handle<BrowserView> browser_view);
virtual void RemoveBrowserView(gin::Handle<BrowserView> browser_view);
virtual void SetTopBrowserView(gin::Handle<BrowserView> browser_view,
gin_helper::Arguments* args);
virtual std::vector<v8::Local<v8::Value>> GetBrowserViews() const;
virtual void ResetBrowserViews();
std::string GetMediaSourceId() const;
v8::Local<v8::Value> GetNativeWindowHandle();
void SetProgressBar(double progress, gin_helper::Arguments* args);
@ -227,7 +218,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
v8::Local<v8::Value> GetContentView() const;
v8::Local<v8::Value> GetParentWindow() const;
std::vector<v8::Local<v8::Object>> GetChildWindows() const;
v8::Local<v8::Value> GetBrowserView(gin_helper::Arguments* args) const;
bool IsModal() const;
// Extra APIs added in JS.
@ -254,9 +244,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
// Helpers.
// Remove BrowserView.
void ResetBrowserView();
// Remove this window from parent window's |child_windows_|.
void RemoveFromParentChildWindows();
@ -274,7 +261,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
#endif
v8::Global<v8::Value> content_view_;
std::vector<v8::Global<v8::Value>> browser_views_;
v8::Global<v8::Value> menu_;
v8::Global<v8::Value> parent_window_;
KeyWeakMap<int> child_windows_;

View file

@ -1,231 +0,0 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/electron_api_browser_view.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
#include "content/public/browser/render_widget_host_view.h"
#include "shell/browser/api/electron_api_base_window.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_browser_view.h"
#include "shell/browser/ui/drag_util.h"
#include "shell/browser/web_contents_preferences.h"
#include "shell/common/color_util.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/geometry/rect.h"
namespace gin {
template <>
struct Converter<electron::AutoResizeFlags> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
electron::AutoResizeFlags* auto_resize_flags) {
gin_helper::Dictionary params;
if (!ConvertFromV8(isolate, val, &params)) {
return false;
}
uint8_t flags = 0;
bool width = false;
if (params.Get("width", &width) && width) {
flags |= electron::kAutoResizeWidth;
}
bool height = false;
if (params.Get("height", &height) && height) {
flags |= electron::kAutoResizeHeight;
}
bool horizontal = false;
if (params.Get("horizontal", &horizontal) && horizontal) {
flags |= electron::kAutoResizeHorizontal;
}
bool vertical = false;
if (params.Get("vertical", &vertical) && vertical) {
flags |= electron::kAutoResizeVertical;
}
*auto_resize_flags = static_cast<electron::AutoResizeFlags>(flags);
return true;
}
};
} // namespace gin
namespace {
int32_t GetNextId() {
static int32_t next_id = 1;
return next_id++;
}
} // namespace
namespace electron::api {
gin::WrapperInfo BrowserView::kWrapperInfo = {gin::kEmbedderNativeGin};
BrowserView::BrowserView(gin::Arguments* args,
const gin_helper::Dictionary& options)
: id_(GetNextId()) {
auto web_preferences = gin_helper::Dictionary::CreateEmpty(args->isolate());
options.Get(options::kWebPreferences, &web_preferences);
web_preferences.Set("type", "browserView");
v8::Local<v8::Value> value;
// Copy the webContents option to webPreferences.
if (options.Get("webContents", &value)) {
web_preferences.SetHidden("webContents", value);
}
auto web_contents =
WebContents::CreateFromWebPreferences(args->isolate(), web_preferences);
web_contents_.Reset(args->isolate(), web_contents.ToV8());
api_web_contents_ = web_contents.get();
api_web_contents_->AddObserver(this);
Observe(web_contents->web_contents());
view_.reset(
NativeBrowserView::Create(api_web_contents_->inspectable_web_contents()));
}
void BrowserView::SetOwnerWindow(BaseWindow* window) {
// Ensure WebContents and BrowserView owner windows are in sync.
if (web_contents())
web_contents()->SetOwnerWindow(window ? window->window() : nullptr);
owner_window_ = window ? window->GetWeakPtr() : nullptr;
}
int BrowserView::NonClientHitTest(const gfx::Point& point) {
gfx::Rect bounds = GetBounds();
gfx::Point local_point(point.x() - bounds.x(), point.y() - bounds.y());
SkRegion* region = api_web_contents_->draggable_region();
if (region && region->contains(local_point.x(), local_point.y()))
return HTCAPTION;
return HTNOWHERE;
}
BrowserView::~BrowserView() {
if (web_contents()) { // destroy() called without closing WebContents
web_contents()->RemoveObserver(this);
web_contents()->Destroy();
}
}
void BrowserView::WebContentsDestroyed() {
if (owner_window())
owner_window()->window()->RemoveDraggableRegionProvider(this);
api_web_contents_ = nullptr;
web_contents_.Reset();
Unpin();
}
void BrowserView::OnCloseContents() {
api_web_contents_ = nullptr;
}
// static
gin::Handle<BrowserView> BrowserView::New(gin_helper::ErrorThrower thrower,
gin::Arguments* args) {
if (!Browser::Get()->is_ready()) {
thrower.ThrowError("Cannot create BrowserView before app is ready");
return gin::Handle<BrowserView>();
}
auto options = gin::Dictionary::CreateEmpty(args->isolate());
args->GetNext(&options);
auto handle =
gin::CreateHandle(args->isolate(), new BrowserView(args, options));
handle->Pin(args->isolate());
return handle;
}
void BrowserView::SetAutoResize(AutoResizeFlags flags) {
view_->SetAutoResizeFlags(flags);
}
void BrowserView::SetBounds(const gfx::Rect& bounds) {
view_->SetBounds(bounds);
}
gfx::Rect BrowserView::GetBounds() {
return view_->GetBounds();
}
void BrowserView::SetBackgroundColor(const std::string& color_name) {
SkColor color = ParseCSSColor(color_name);
view_->SetBackgroundColor(color);
if (web_contents()) {
auto* wc = web_contents()->web_contents();
wc->SetPageBaseBackgroundColor(ParseCSSColor(color_name));
auto* const rwhv = wc->GetRenderWidgetHostView();
if (rwhv) {
rwhv->SetBackgroundColor(color);
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
->SetContentBackgroundColor(color);
}
// Ensure new color is stored in webPreferences, otherwise
// the color will be reset on the next load via HandleNewRenderFrame.
auto* web_preferences = WebContentsPreferences::From(wc);
if (web_preferences)
web_preferences->SetBackgroundColor(color);
}
}
v8::Local<v8::Value> BrowserView::GetWebContents(v8::Isolate* isolate) {
if (web_contents_.IsEmpty()) {
return v8::Null(isolate);
}
return v8::Local<v8::Value>::New(isolate, web_contents_);
}
// static
void BrowserView::FillObjectTemplate(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> templ) {
gin::ObjectTemplateBuilder(isolate, GetClassName(), templ)
.SetMethod("setAutoResize", &BrowserView::SetAutoResize)
.SetMethod("setBounds", &BrowserView::SetBounds)
.SetMethod("getBounds", &BrowserView::GetBounds)
.SetMethod("setBackgroundColor", &BrowserView::SetBackgroundColor)
.SetProperty("webContents", &BrowserView::GetWebContents)
.Build();
}
const char* BrowserView::GetTypeName() {
return GetClassName();
}
} // namespace electron::api
namespace {
using electron::api::BrowserView;
void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
gin_helper::Dictionary dict(isolate, exports);
dict.Set("BrowserView", BrowserView::GetConstructor(context));
}
} // namespace
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_browser_view, Initialize)

View file

@ -1,98 +0,0 @@
// Copyright (c) 2017 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_
#include <memory>
#include <string>
#include "base/memory/raw_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "gin/handle.h"
#include "gin/wrappable.h"
#include "shell/browser/draggable_region_provider.h"
#include "shell/browser/extended_web_contents_observer.h"
#include "shell/browser/native_browser_view.h"
#include "shell/browser/native_window.h"
#include "shell/common/api/api.mojom.h"
#include "shell/common/gin_helper/constructible.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/pinnable.h"
namespace gfx {
class Rect;
}
namespace gin_helper {
class Dictionary;
}
namespace electron::api {
class WebContents;
class BaseWindow;
class BrowserView : public gin::Wrappable<BrowserView>,
public gin_helper::Constructible<BrowserView>,
public gin_helper::Pinnable<BrowserView>,
public content::WebContentsObserver,
public ExtendedWebContentsObserver,
public DraggableRegionProvider {
public:
// gin_helper::Constructible
static gin::Handle<BrowserView> New(gin_helper::ErrorThrower thrower,
gin::Arguments* args);
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>);
static const char* GetClassName() { return "BrowserView"; }
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
const char* GetTypeName() override;
WebContents* web_contents() const { return api_web_contents_; }
NativeBrowserView* view() const { return view_.get(); }
BaseWindow* owner_window() const { return owner_window_.get(); }
void SetOwnerWindow(BaseWindow* window);
int32_t ID() const { return id_; }
int NonClientHitTest(const gfx::Point& point) override;
// disable copy
BrowserView(const BrowserView&) = delete;
BrowserView& operator=(const BrowserView&) = delete;
gfx::Rect GetBounds();
void SetBounds(const gfx::Rect& bounds);
protected:
BrowserView(gin::Arguments* args, const gin_helper::Dictionary& options);
~BrowserView() override;
// content::WebContentsObserver:
void WebContentsDestroyed() override;
// ExtendedWebContentsObserver:
void OnCloseContents() override;
private:
void SetAutoResize(AutoResizeFlags flags);
void SetBackgroundColor(const std::string& color_name);
v8::Local<v8::Value> GetWebContents(v8::Isolate*);
v8::Global<v8::Value> web_contents_;
class raw_ptr<WebContents> api_web_contents_ = nullptr;
std::unique_ptr<NativeBrowserView> view_;
base::WeakPtr<BaseWindow> owner_window_;
int32_t id_;
};
} // namespace electron::api
#endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_

View file

@ -13,7 +13,6 @@
#include "content/public/common/color_parser.h"
#include "shell/browser/api/electron_api_web_contents_view.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_browser_view.h"
#include "shell/browser/web_contents_preferences.h"
#include "shell/browser/window_list.h"
#include "shell/common/color_util.h"
@ -70,6 +69,9 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
web_preferences.SetHidden("webContents", value);
}
if (!web_preferences.Has(options::kShow))
web_preferences.Set(options::kShow, true);
// Creates the WebContentsView.
gin::Handle<WebContentsView> web_contents_view =
WebContentsView::Create(isolate, web_preferences);
@ -101,8 +103,6 @@ BrowserWindow::~BrowserWindow() {
// Cleanup the observers if user destroyed this instance directly instead of
// gracefully closing content::WebContents.
api_web_contents_->RemoveObserver(this);
// Destroy the WebContents.
OnCloseContents();
api_web_contents_->Destroy();
}
}
@ -129,10 +129,6 @@ void BrowserWindow::WebContentsDestroyed() {
CloseImmediately();
}
void BrowserWindow::OnCloseContents() {
BaseWindow::ResetBrowserViews();
}
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
window_unresponsive_closure_.Cancel();
Emit("responsive");
@ -187,26 +183,6 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
// Required to make beforeunload handler work.
api_web_contents_->NotifyUserActivation();
// Trigger beforeunload events for associated BrowserViews.
for (NativeBrowserView* view : window_->browser_views()) {
auto* iwc = view->GetInspectableWebContents();
if (!iwc)
continue;
auto* vwc = iwc->GetWebContents();
auto* api_web_contents = api::WebContents::From(vwc);
// Required to make beforeunload handler work.
if (api_web_contents)
api_web_contents->NotifyUserActivation();
if (vwc) {
if (vwc->NeedToFireBeforeUnloadOrUnloadEvents()) {
vwc->DispatchBeforeUnload(false /* auto_cancel */);
}
}
}
if (web_contents()->NeedToFireBeforeUnloadOrUnloadEvents()) {
web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
} else {
@ -288,16 +264,10 @@ void BrowserWindow::Blur() {
void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
BaseWindow::SetBackgroundColor(color_name);
SkColor color = ParseCSSColor(color_name);
web_contents()->SetPageBaseBackgroundColor(color);
auto* rwhv = web_contents()->GetRenderWidgetHostView();
if (rwhv) {
rwhv->SetBackgroundColor(color);
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
->SetContentBackgroundColor(color);
}
// Also update the web preferences object otherwise the view will be reset on
// the next load URL call
if (api_web_contents_) {
api_web_contents_->SetBackgroundColor(color);
// Also update the web preferences object otherwise the view will be reset
// on the next load URL call
auto* web_preferences =
WebContentsPreferences::From(api_web_contents_->web_contents());
if (web_preferences) {
@ -306,13 +276,6 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
}
}
void BrowserWindow::SetBrowserView(
absl::optional<gin::Handle<BrowserView>> browser_view) {
BaseWindow::ResetBrowserViews();
if (browser_view)
BaseWindow::AddBrowserView(*browser_view);
}
void BrowserWindow::FocusOnWebView() {
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
}

View file

@ -49,7 +49,6 @@ class BrowserWindow : public BaseWindow,
void WebContentsDestroyed() override;
// ExtendedWebContentsObserver:
void OnCloseContents() override;
void OnSetContentBounds(const gfx::Rect& rect) override;
void OnActivateContents() override;
void OnPageTitleUpdated(const std::u16string& title,
@ -69,8 +68,6 @@ class BrowserWindow : public BaseWindow,
void Focus() override;
void Blur() override;
void SetBackgroundColor(const std::string& color_name) override;
void SetBrowserView(
absl::optional<gin::Handle<BrowserView>> browser_view) override;
void OnWindowShow() override;
void OnWindowHide() override;

View file

@ -4,34 +4,357 @@
#include "shell/browser/api/electron_api_view.h"
#include <algorithm>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include "gin/data_object_builder.h"
#include "gin/wrappable.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "ui/views/background.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/layout_manager_base.h"
#if BUILDFLAG(IS_MAC)
#include "shell/browser/animation_util.h"
#endif
namespace gin {
template <>
struct Converter<views::ChildLayout> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
views::ChildLayout* out) {
gin_helper::Dictionary dict;
if (!gin::ConvertFromV8(isolate, val, &dict))
return false;
gin::Handle<electron::api::View> view;
if (!dict.Get("view", &view))
return false;
out->child_view = view->view();
if (dict.Has("bounds"))
dict.Get("bounds", &out->bounds);
out->visible = true;
if (dict.Has("visible"))
dict.Get("visible", &out->visible);
return true;
}
};
template <>
struct Converter<views::ProposedLayout> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
views::ProposedLayout* out) {
gin_helper::Dictionary dict;
if (!gin::ConvertFromV8(isolate, val, &dict))
return false;
if (!dict.Get("size", &out->host_size))
return false;
if (!dict.Get("layouts", &out->child_layouts))
return false;
return true;
}
};
template <>
struct Converter<views::LayoutOrientation> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
views::LayoutOrientation* out) {
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (orientation == "horizontal") {
*out = views::LayoutOrientation::kHorizontal;
} else if (orientation == "vertical") {
*out = views::LayoutOrientation::kVertical;
} else {
return false;
}
return true;
}
};
template <>
struct Converter<views::LayoutAlignment> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
views::LayoutAlignment* out) {
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (orientation == "start") {
*out = views::LayoutAlignment::kStart;
} else if (orientation == "center") {
*out = views::LayoutAlignment::kCenter;
} else if (orientation == "end") {
*out = views::LayoutAlignment::kEnd;
} else if (orientation == "stretch") {
*out = views::LayoutAlignment::kStretch;
} else if (orientation == "baseline") {
*out = views::LayoutAlignment::kBaseline;
} else {
return false;
}
return true;
}
};
template <>
struct Converter<views::FlexAllocationOrder> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
views::FlexAllocationOrder* out) {
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
if (orientation == "normal") {
*out = views::FlexAllocationOrder::kNormal;
} else if (orientation == "reverse") {
*out = views::FlexAllocationOrder::kReverse;
} else {
return false;
}
return true;
}
};
template <>
struct Converter<views::SizeBound> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const views::SizeBound& in) {
if (in.is_bounded())
return v8::Integer::New(isolate, in.value());
return v8::Number::New(isolate, std::numeric_limits<double>::infinity());
}
};
template <>
struct Converter<views::SizeBounds> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const views::SizeBounds& in) {
return gin::DataObjectBuilder(isolate)
.Set("width", in.width())
.Set("height", in.height())
.Build();
}
};
} // namespace gin
namespace electron::api {
using LayoutCallback = base::RepeatingCallback<views::ProposedLayout(
const views::SizeBounds& size_bounds)>;
class JSLayoutManager : public views::LayoutManagerBase {
public:
explicit JSLayoutManager(LayoutCallback layout_callback)
: layout_callback_(std::move(layout_callback)) {}
~JSLayoutManager() override {}
views::ProposedLayout CalculateProposedLayout(
const views::SizeBounds& size_bounds) const override {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
return layout_callback_.Run(size_bounds);
}
private:
LayoutCallback layout_callback_;
};
View::View(views::View* view) : view_(view) {
view_->set_owned_by_client();
view_->AddObserver(this);
}
View::View() : View(new views::View()) {}
View::~View() {
if (!view_)
return;
view_->RemoveObserver(this);
if (delete_view_)
delete view_;
}
void View::AddChildViewAt(gin::Handle<View> child,
absl::optional<size_t> maybe_index) {
// TODO(nornagon): !view_ is only for supporting the weird case of
// WebContentsView's view being deleted when the underlying WebContents is
// destroyed (on non-Mac). We should fix that so that WebContentsView always
// has a View, possibly a wrapper view around the underlying platform View.
if (!view_)
return;
size_t index =
std::min(child_views_.size(), maybe_index.value_or(child_views_.size()));
child_views_.emplace(child_views_.begin() + index, // index
isolate(), child->GetWrapper()); // v8::Global(args...)
#if BUILDFLAG(IS_MAC)
// Disable the implicit CALayer animations that happen by default when adding
// or removing sublayers.
// See
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/ReactingtoLayerChanges/ReactingtoLayerChanges.html
// and https://github.com/electron/electron/pull/14911
// TODO(nornagon): Disabling these CALayer animations (which are specific to
// WebContentsView, I think) seems like this is something that remote_cocoa
// or views should be taking care of, but isn't. This should be pushed
// upstream.
ScopedCAActionDisabler disable_animations;
#endif
view_->AddChildViewAt(child->view(), index);
}
void View::RemoveChildView(gin::Handle<View> child) {
if (!view_)
return;
if (!child->view())
return;
auto it = std::find(child_views_.begin(), child_views_.end(), child.ToV8());
if (it != child_views_.end()) {
#if BUILDFLAG(IS_MAC)
ScopedCAActionDisabler disable_animations;
#endif
view_->RemoveChildView(child->view());
child_views_.erase(it);
}
}
void View::SetBounds(const gfx::Rect& bounds) {
if (!view_)
return;
view_->SetBoundsRect(bounds);
}
gfx::Rect View::GetBounds() {
if (!view_)
return gfx::Rect();
return view_->bounds();
}
void View::SetLayout(v8::Isolate* isolate, v8::Local<v8::Object> value) {
if (!view_)
return;
gin_helper::Dictionary dict(isolate, value);
LayoutCallback calculate_proposed_layout;
if (dict.Get("calculateProposedLayout", &calculate_proposed_layout)) {
view_->SetLayoutManager(std::make_unique<JSLayoutManager>(
std::move(calculate_proposed_layout)));
} else {
auto* layout =
view_->SetLayoutManager(std::make_unique<views::FlexLayout>());
views::LayoutOrientation orientation;
if (dict.Get("orientation", &orientation))
layout->SetOrientation(orientation);
views::LayoutAlignment main_axis_alignment;
if (dict.Get("mainAxisAlignment", &main_axis_alignment))
layout->SetMainAxisAlignment(main_axis_alignment);
views::LayoutAlignment cross_axis_alignment;
if (dict.Get("crossAxisAlignment", &cross_axis_alignment))
layout->SetCrossAxisAlignment(cross_axis_alignment);
gfx::Insets interior_margin;
if (dict.Get("interiorMargin", &interior_margin))
layout->SetInteriorMargin(interior_margin);
int minimum_cross_axis_size;
if (dict.Get("minimumCrossAxisSize", &minimum_cross_axis_size))
layout->SetMinimumCrossAxisSize(minimum_cross_axis_size);
bool collapse_margins;
if (dict.Has("collapseMargins") &&
dict.Get("collapseMargins", &collapse_margins))
layout->SetCollapseMargins(collapse_margins);
bool include_host_insets_in_layout;
if (dict.Has("includeHostInsetsInLayout") &&
dict.Get("includeHostInsetsInLayout", &include_host_insets_in_layout))
layout->SetIncludeHostInsetsInLayout(include_host_insets_in_layout);
bool ignore_default_main_axis_margins;
if (dict.Has("ignoreDefaultMainAxisMargins") &&
dict.Get("ignoreDefaultMainAxisMargins",
&ignore_default_main_axis_margins))
layout->SetIgnoreDefaultMainAxisMargins(ignore_default_main_axis_margins);
views::FlexAllocationOrder flex_allocation_order;
if (dict.Get("flexAllocationOrder", &flex_allocation_order))
layout->SetFlexAllocationOrder(flex_allocation_order);
}
}
std::vector<v8::Local<v8::Value>> View::GetChildren() {
std::vector<v8::Local<v8::Value>> ret;
ret.reserve(child_views_.size());
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
for (auto& child_view : child_views_)
ret.push_back(child_view.Get(isolate));
return ret;
}
void View::SetBackgroundColor(absl::optional<WrappedSkColor> color) {
if (!view_)
return;
view_->SetBackground(color ? views::CreateSolidBackground(*color) : nullptr);
}
void View::SetVisible(bool visible) {
if (!view_)
return;
view_->SetVisible(visible);
}
void View::OnViewBoundsChanged(views::View* observed_view) {
Emit("bounds-changed");
}
void View::OnViewIsDeleting(views::View* observed_view) {
DCHECK_EQ(observed_view, view_);
view_ = nullptr;
}
// static
gin_helper::WrappableBase* View::New(gin::Arguments* args) {
auto* view = new View();
View* view = new View();
view->InitWithArgs(args);
return view;
}
// static
v8::Local<v8::Function> View::GetConstructor(v8::Isolate* isolate) {
static base::NoDestructor<v8::Global<v8::Function>> constructor;
if (constructor.get()->IsEmpty()) {
constructor->Reset(isolate, gin_helper::CreateConstructor<View>(
isolate, base::BindRepeating(&View::New)));
}
return v8::Local<v8::Function>::New(isolate, *constructor.get());
}
// static
gin::Handle<View> View::Create(v8::Isolate* isolate) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Object> obj;
if (GetConstructor(isolate)->NewInstance(context, 0, nullptr).ToLocal(&obj)) {
gin::Handle<View> view;
if (gin::ConvertFromV8(isolate, obj, &view))
return view;
}
return gin::Handle<View>();
}
// static
void View::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(gin::StringToV8(isolate, "View"));
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("addChildView", &View::AddChildViewAt)
.SetMethod("removeChildView", &View::RemoveChildView)
.SetProperty("children", &View::GetChildren)
.SetMethod("setBounds", &View::SetBounds)
.SetMethod("getBounds", &View::GetBounds)
.SetMethod("setBackgroundColor", &View::SetBackgroundColor)
.SetMethod("setLayout", &View::SetLayout)
.SetMethod("setVisible", &View::SetVisible);
}
} // namespace electron::api
@ -45,14 +368,9 @@ void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
View::SetConstructor(isolate, base::BindRepeating(&View::New));
gin_helper::Dictionary constructor(
isolate,
View::GetConstructor(isolate)->GetFunction(context).ToLocalChecked());
gin_helper::Dictionary dict(isolate, exports);
dict.Set("View", constructor);
dict.Set("View", View::GetConstructor(isolate));
}
} // namespace

View file

@ -7,18 +7,39 @@
#include "base/memory/raw_ptr.h"
#include "gin/handle.h"
#include "shell/common/gin_helper/wrappable.h"
#include "shell/common/color_util.h"
#include "shell/common/gin_helper/event_emitter.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
#include "v8/include/v8-value.h"
namespace electron::api {
class View : public gin_helper::Wrappable<View> {
class View : public gin_helper::EventEmitter<View>, public views::ViewObserver {
public:
static gin_helper::WrappableBase* New(gin::Arguments* args);
static gin::Handle<View> Create(v8::Isolate* isolate);
// Return the cached constructor function.
static v8::Local<v8::Function> GetConstructor(v8::Isolate* isolate);
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
void AddChildViewAt(gin::Handle<View> child, absl::optional<size_t> index);
void RemoveChildView(gin::Handle<View> child);
void SetBounds(const gfx::Rect& bounds);
gfx::Rect GetBounds();
void SetLayout(v8::Isolate* isolate, v8::Local<v8::Object> value);
std::vector<v8::Local<v8::Value>> GetChildren();
void SetBackgroundColor(absl::optional<WrappedSkColor> color);
void SetVisible(bool visible);
// views::ViewObserver
void OnViewBoundsChanged(views::View* observed_view) override;
void OnViewIsDeleting(views::View* observed_view) override;
views::View* view() const { return view_; }
// disable copy
@ -34,6 +55,8 @@ class View : public gin_helper::Wrappable<View> {
void set_delete_view(bool should) { delete_view_ = should; }
private:
std::vector<v8::Global<v8::Object>> child_views_;
bool delete_view_ = true;
raw_ptr<views::View> view_ = nullptr;
};

View file

@ -717,12 +717,6 @@ bool IsDevToolsFileSystemAdded(content::WebContents* web_contents,
file_system_path);
}
void SetBackgroundColor(content::RenderWidgetHostView* rwhv, SkColor color) {
rwhv->SetBackgroundColor(color);
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
->SetContentBackgroundColor(color);
}
content::RenderFrameHost* GetRenderFrameHost(
content::NavigationHandle* navigation_handle) {
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
@ -739,7 +733,6 @@ content::RenderFrameHost* GetRenderFrameHost(
return frame_host;
}
} // namespace
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
@ -839,10 +832,7 @@ WebContents::WebContents(v8::Isolate* isolate,
// Whether to enable DevTools.
options.Get("devTools", &enable_devtools_);
// BrowserViews are not attached to a window initially so they should start
// off as hidden. This is also important for compositor recycling. See:
// https://github.com/electron/electron/pull/21372
bool initially_shown = type_ != Type::kBrowserView;
bool initially_shown = true;
options.Get(options::kShow, &initially_shown);
// Obtain the session.
@ -1287,13 +1277,11 @@ content::WebContents* WebContents::OpenURLFromTab(
void WebContents::BeforeUnloadFired(content::WebContents* tab,
bool proceed,
bool* proceed_to_fire_unload) {
if (type_ == Type::kBrowserWindow || type_ == Type::kOffScreen ||
type_ == Type::kBrowserView)
*proceed_to_fire_unload = proceed;
else
*proceed_to_fire_unload = true;
// Note that Chromium does not emit this for navigations.
Emit("before-unload-fired", proceed);
// Emit returns true if preventDefault() was called, so !Emit will be true if
// the event should proceed.
*proceed_to_fire_unload = !Emit("-before-unload-fired", proceed);
}
void WebContents::SetContentsBounds(content::WebContents* source,
@ -1312,12 +1300,7 @@ void WebContents::CloseContents(content::WebContents* source) {
autofill_driver_factory->CloseAllPopups();
}
for (ExtendedWebContentsObserver& observer : observers_)
observer.OnCloseContents();
// This is handled by the embedder frame.
if (!IsGuest())
Destroy();
Destroy();
}
void WebContents::ActivateContents(content::WebContents* source) {
@ -1599,9 +1582,53 @@ void WebContents::RequestMediaAccessPermission(
permission_helper->RequestMediaAccessPermission(request, std::move(callback));
}
const void* const kJavaScriptDialogManagerKey = &kJavaScriptDialogManagerKey;
content::JavaScriptDialogManager* WebContents::GetJavaScriptDialogManager(
content::WebContents* source) {
return this;
// Indirect these delegate methods through a helper object whose lifetime is
// bound to that of the content::WebContents. This prevents the
// content::WebContents from calling methods on the Electron WebContents in
// the event that the Electron one is destroyed before the content one, as
// happens sometimes during shutdown or when webviews are involved.
class JSDialogManagerHelper : public content::JavaScriptDialogManager,
public base::SupportsUserData::Data {
public:
void RunJavaScriptDialog(content::WebContents* web_contents,
content::RenderFrameHost* rfh,
content::JavaScriptDialogType dialog_type,
const std::u16string& message_text,
const std::u16string& default_prompt_text,
DialogClosedCallback callback,
bool* did_suppress_message) override {
auto* wc = WebContents::From(web_contents);
if (wc)
wc->RunJavaScriptDialog(web_contents, rfh, dialog_type, message_text,
default_prompt_text, std::move(callback),
did_suppress_message);
}
void RunBeforeUnloadDialog(content::WebContents* web_contents,
content::RenderFrameHost* rfh,
bool is_reload,
DialogClosedCallback callback) override {
auto* wc = WebContents::From(web_contents);
if (wc)
wc->RunBeforeUnloadDialog(web_contents, rfh, is_reload,
std::move(callback));
}
void CancelDialogs(content::WebContents* web_contents,
bool reset_state) override {
auto* wc = WebContents::From(web_contents);
if (wc)
wc->CancelDialogs(web_contents, reset_state);
}
};
if (!source->GetUserData(kJavaScriptDialogManagerKey))
source->SetUserData(kJavaScriptDialogManagerKey,
std::make_unique<JSDialogManagerHelper>());
return static_cast<JSDialogManagerHelper*>(
source->GetUserData(kJavaScriptDialogManagerKey));
}
void WebContents::OnAudioStateChanged(bool audible) {
@ -1628,17 +1655,8 @@ void WebContents::HandleNewRenderFrame(
// Set the background color of RenderWidgetHostView.
auto* web_preferences = WebContentsPreferences::From(web_contents());
if (web_preferences) {
auto maybe_color = web_preferences->GetBackgroundColor();
bool guest = IsGuest() || type_ == Type::kBrowserView;
// If webPreferences has no color stored we need to explicitly set guest
// webContents background color to transparent.
auto bg_color =
maybe_color.value_or(guest ? SK_ColorTRANSPARENT : SK_ColorWHITE);
web_contents()->SetPageBaseBackgroundColor(bg_color);
SetBackgroundColor(rwhv, bg_color);
}
if (web_preferences)
SetBackgroundColor(web_preferences->GetBackgroundColor());
if (!background_throttling_)
render_frame_host->GetRenderViewHost()->SetSchedulerThrottling(false);
@ -2245,6 +2263,11 @@ void WebContents::SetOwnerWindow(NativeWindow* owner_window) {
SetOwnerWindow(GetWebContents(), owner_window);
}
void WebContents::SetOwnerBaseWindow(absl::optional<BaseWindow*> owner_window) {
SetOwnerWindow(GetWebContents(),
owner_window ? (*owner_window)->window() : nullptr);
}
void WebContents::SetOwnerWindow(content::WebContents* web_contents,
NativeWindow* owner_window) {
if (owner_window_) {
@ -3754,6 +3777,22 @@ void WebContents::SetImageAnimationPolicy(const std::string& new_policy) {
web_contents()->OnWebPreferencesChanged();
}
void WebContents::SetBackgroundColor(absl::optional<SkColor> maybe_color) {
web_contents()->SetPageBaseBackgroundColor(maybe_color);
content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
if (!rfh)
return;
content::RenderWidgetHostView* rwhv = rfh->GetView();
if (rwhv) {
SkColor color =
maybe_color.value_or(IsGuest() ? SK_ColorTRANSPARENT : SK_ColorWHITE);
rwhv->SetBackgroundColor(color);
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
->SetContentBackgroundColor(color);
}
}
void WebContents::OnInputEvent(const blink::WebInputEvent& event) {
Emit("input-event", event);
}
@ -4414,6 +4453,7 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
.SetProperty("debugger", &WebContents::Debugger)
.SetProperty("mainFrame", &WebContents::MainFrame)
.SetProperty("opener", &WebContents::Opener)
.SetMethod("_setOwnerWindow", &WebContents::SetOwnerBaseWindow)
.Build();
}
@ -4497,14 +4537,8 @@ gin::Handle<WebContents> WebContents::CreateFromWebPreferences(
if (gin::ConvertFromV8(isolate, web_preferences.GetHandle(),
&web_preferences_dict)) {
existing_preferences->SetFromDictionary(web_preferences_dict);
absl::optional<SkColor> color =
existing_preferences->GetBackgroundColor();
web_contents->web_contents()->SetPageBaseBackgroundColor(color);
// Because web preferences don't recognize transparency,
// only set rwhv background color if a color exists
auto* rwhv = web_contents->web_contents()->GetRenderWidgetHostView();
if (rwhv && color.has_value())
SetBackgroundColor(rwhv, color.value());
web_contents->SetBackgroundColor(
existing_preferences->GetBackgroundColor());
}
} else {
// Create one if not.

View file

@ -97,6 +97,8 @@ class OffScreenWebContentsView;
namespace api {
class BaseWindow;
// Wrapper around the content::WebContents.
class WebContents : public ExclusiveAccessContext,
public gin::Wrappable<WebContents>,
@ -115,7 +117,9 @@ class WebContents : public ExclusiveAccessContext,
enum class Type {
kBackgroundPage, // An extension background page.
kBrowserWindow, // Used by BrowserWindow.
kBrowserView, // Used by BrowserView.
kBrowserView, // Used by the JS implementation of BrowserView for
// backwards compatibility. Otherwise identical to
// kBrowserWindow.
kRemote, // Thin wrap around an existing WebContents.
kWebView, // Used by <webview>.
kOffScreen, // Used for offscreen rendering
@ -399,6 +403,7 @@ class WebContents : public ExclusiveAccessContext,
void SetOwnerWindow(NativeWindow* owner_window);
void SetOwnerWindow(content::WebContents* web_contents,
NativeWindow* owner_window);
void SetOwnerBaseWindow(absl::optional<BaseWindow*> owner_window);
// Returns the WebContents managed by this delegate.
content::WebContents* GetWebContents() const;
@ -469,6 +474,8 @@ class WebContents : public ExclusiveAccessContext,
void CancelDialogs(content::WebContents* web_contents,
bool reset_state) override;
void SetBackgroundColor(absl::optional<SkColor> color);
SkRegion* draggable_region() {
return force_non_draggable_ ? nullptr : draggable_region_.get();
}

View file

@ -5,17 +5,24 @@
#include "shell/browser/api/electron_api_web_contents_view.h"
#include "base/no_destructor.h"
#include "gin/data_object_builder.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_window.h"
#include "shell/browser/ui/inspectable_web_contents_view.h"
#include "shell/browser/web_contents_preferences.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/constructor.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "shell/common/options_switches.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/base/hit_test.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(IS_MAC)
#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
@ -40,6 +47,10 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
// managed by InspectableWebContents.
set_delete_view(false);
#endif
view()->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded));
Observe(web_contents->web_contents());
}
@ -49,7 +60,24 @@ WebContentsView::~WebContentsView() {
}
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
return gin::CreateHandle(isolate, api_web_contents_.get());
if (api_web_contents_)
return gin::CreateHandle(isolate, api_web_contents_.get());
else
return gin::Handle<WebContents>();
}
void WebContentsView::SetBackgroundColor(absl::optional<WrappedSkColor> color) {
View::SetBackgroundColor(color);
if (api_web_contents_) {
api_web_contents_->SetBackgroundColor(color);
// Also update the web preferences object otherwise the view will be reset
// on the next load URL call
auto* web_preferences =
WebContentsPreferences::From(api_web_contents_->web_contents());
if (web_preferences) {
web_preferences->SetBackgroundColor(color);
}
}
}
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
@ -66,16 +94,40 @@ void WebContentsView::WebContentsDestroyed() {
web_contents_.Reset();
}
void WebContentsView::OnViewAddedToWidget(views::View* observed_view) {
DCHECK_EQ(observed_view, view());
views::Widget* widget = view()->GetWidget();
auto* native_window = static_cast<NativeWindow*>(
widget->GetNativeWindowProperty(electron::kElectronNativeWindowKey));
if (!native_window)
return;
native_window->AddDraggableRegionProvider(this);
}
void WebContentsView::OnViewRemovedFromWidget(views::View* observed_view) {
DCHECK_EQ(observed_view, view());
views::Widget* widget = view()->GetWidget();
auto* native_window = static_cast<NativeWindow*>(
widget->GetNativeWindowProperty(kElectronNativeWindowKey));
if (!native_window)
return;
native_window->RemoveDraggableRegionProvider(this);
}
// static
gin::Handle<WebContentsView> WebContentsView::Create(
v8::Isolate* isolate,
const gin_helper::Dictionary& web_preferences) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> arg = gin::ConvertToV8(isolate, web_preferences);
v8::Local<v8::Object> obj;
if (GetConstructor(isolate)->NewInstance(context, 1, &arg).ToLocal(&obj)) {
v8::Local<v8::Value> arg = gin::DataObjectBuilder(isolate)
.Set("webPreferences", web_preferences)
.Build();
v8::Local<v8::Object> web_contents_view_obj;
if (GetConstructor(isolate)
->NewInstance(context, 1, &arg)
.ToLocal(&web_contents_view_obj)) {
gin::Handle<WebContentsView> web_contents_view;
if (gin::ConvertFromV8(isolate, obj, &web_contents_view))
if (gin::ConvertFromV8(isolate, web_contents_view_obj, &web_contents_view))
return web_contents_view;
}
return gin::Handle<WebContentsView>();
@ -93,9 +145,30 @@ v8::Local<v8::Function> WebContentsView::GetConstructor(v8::Isolate* isolate) {
}
// static
gin_helper::WrappableBase* WebContentsView::New(
gin_helper::Arguments* args,
const gin_helper::Dictionary& web_preferences) {
gin_helper::WrappableBase* WebContentsView::New(gin_helper::Arguments* args) {
gin_helper::Dictionary web_preferences;
{
v8::Local<v8::Value> options_value;
if (args->GetNext(&options_value)) {
gin_helper::Dictionary options;
if (!gin::ConvertFromV8(args->isolate(), options_value, &options)) {
args->ThrowError("options must be an object");
return nullptr;
}
v8::Local<v8::Value> web_preferences_value;
if (options.Get("webPreferences", &web_preferences_value)) {
if (!gin::ConvertFromV8(args->isolate(), web_preferences_value,
&web_preferences)) {
args->ThrowError("options.webPreferences must be an object");
return nullptr;
}
}
}
}
if (web_preferences.IsEmpty())
web_preferences = gin_helper::Dictionary::CreateEmpty(args->isolate());
if (!web_preferences.Has(options::kShow))
web_preferences.Set(options::kShow, false);
auto web_contents =
WebContents::CreateFromWebPreferences(args->isolate(), web_preferences);
@ -111,6 +184,7 @@ void WebContentsView::BuildPrototype(
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(gin::StringToV8(isolate, "WebContentsView"));
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("setBackgroundColor", &WebContentsView::SetBackgroundColor)
.SetProperty("webContents", &WebContentsView::GetWebContents);
}

View file

@ -36,6 +36,7 @@ class WebContentsView : public View,
// Public APIs.
gin::Handle<WebContents> GetWebContents(v8::Isolate* isolate);
void SetBackgroundColor(absl::optional<WrappedSkColor> color);
int NonClientHitTest(const gfx::Point& point) override;
@ -47,10 +48,12 @@ class WebContentsView : public View,
// content::WebContentsObserver:
void WebContentsDestroyed() override;
// views::ViewObserver
void OnViewAddedToWidget(views::View* view) override;
void OnViewRemovedFromWidget(views::View* view) override;
private:
static gin_helper::WrappableBase* New(
gin_helper::Arguments* args,
const gin_helper::Dictionary& web_preferences);
static gin_helper::WrappableBase* New(gin_helper::Arguments* args);
// Keep a reference to v8 wrapper.
v8::Global<v8::Value> web_contents_;