Skip to content

Commit 8b9f7e5

Browse files
committed
Implement initial, experimental BrowserView API
Right now, `<webview>` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `<webview>` suffers from a [number of problems](https://github.com/electron/electron/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3Awebview%20). To make matters worse, many of these are upstream Chromium bugs instead of Electron-specific bugs. For us at [Figma](https://www.figma.com), the main issue is very slow performance. Despite the upstream improvements to `<webview>` through the OOPIF work, it is probable that there will continue to be `<webview>`-specific bugs in the future. Therefore, this introduces a `<webview>` alternative to called `BrowserView`, which... - is a thin wrapper around `api::WebContents` (so bugs in `BrowserView` will likely also be bugs in `BrowserWindow` web contents) - is instantiated in the main process like `BrowserWindow` (and unlike `<webview>`, which lives in the DOM of a `BrowserWindow` web contents) - needs to be added to a `BrowserWindow` to display something on the screen This implements the most basic API. The API is expected to evolve and change in the near future and has consequently been marked as experimental. Please do not use this API in production unless you are prepared to deal with breaking changes. In the future, we will want to change the API to support multiple `BrowserView`s per window. We will also want to consider z-ordering auto-resizing, and possibly even nested views.
1 parent 779e4e5 commit 8b9f7e5

25 files changed

+665
-23
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) 2017 GitHub, Inc.
2+
// Use of this source code is governed by the MIT license that can be
3+
// found in the LICENSE file.
4+
5+
#include "atom/browser/api/atom_api_browser_view.h"
6+
7+
#include "atom/browser/api/atom_api_web_contents.h"
8+
#include "atom/browser/browser.h"
9+
#include "atom/browser/native_browser_view.h"
10+
#include "atom/common/color_util.h"
11+
#include "atom/common/native_mate_converters/gfx_converter.h"
12+
#include "atom/common/native_mate_converters/value_converter.h"
13+
#include "atom/common/node_includes.h"
14+
#include "atom/common/options_switches.h"
15+
#include "native_mate/constructor.h"
16+
#include "native_mate/dictionary.h"
17+
#include "ui/gfx/geometry/rect.h"
18+
19+
namespace atom {
20+
21+
namespace api {
22+
23+
BrowserView::BrowserView(v8::Isolate* isolate,
24+
v8::Local<v8::Object> wrapper,
25+
const mate::Dictionary& options)
26+
: api_web_contents_(nullptr) {
27+
Init(isolate, wrapper, options);
28+
}
29+
30+
void BrowserView::Init(v8::Isolate* isolate,
31+
v8::Local<v8::Object> wrapper,
32+
const mate::Dictionary& options) {
33+
mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate);
34+
options.Get(options::kWebPreferences, &web_preferences);
35+
web_preferences.Set("isBrowserView", true);
36+
mate::Handle<class WebContents> web_contents =
37+
WebContents::Create(isolate, web_preferences);
38+
39+
web_contents_.Reset(isolate, web_contents.ToV8());
40+
api_web_contents_ = web_contents.get();
41+
42+
view_.reset(NativeBrowserView::Create(
43+
api_web_contents_->managed_web_contents()->GetView()));
44+
45+
InitWith(isolate, wrapper);
46+
}
47+
48+
BrowserView::~BrowserView() {
49+
api_web_contents_->DestroyWebContents();
50+
}
51+
52+
// static
53+
mate::WrappableBase* BrowserView::New(mate::Arguments* args) {
54+
if (!Browser::Get()->is_ready()) {
55+
args->ThrowError("Cannot create BrowserView before app is ready");
56+
return nullptr;
57+
}
58+
59+
if (args->Length() > 1) {
60+
args->ThrowError("Too many arguments");
61+
return nullptr;
62+
}
63+
64+
mate::Dictionary options;
65+
if (!(args->Length() == 1 && args->GetNext(&options))) {
66+
options = mate::Dictionary::CreateEmpty(args->isolate());
67+
}
68+
69+
return new BrowserView(args->isolate(), args->GetThis(), options);
70+
}
71+
72+
int32_t BrowserView::ID() const {
73+
return weak_map_id();
74+
}
75+
76+
void BrowserView::SetBounds(const gfx::Rect& bounds) {
77+
view_->SetBounds(bounds);
78+
}
79+
80+
void BrowserView::SetBackgroundColor(const std::string& color_name) {
81+
view_->SetBackgroundColor(ParseHexColor(color_name));
82+
}
83+
84+
v8::Local<v8::Value> BrowserView::WebContents() {
85+
if (web_contents_.IsEmpty()) {
86+
return v8::Null(isolate());
87+
}
88+
89+
return v8::Local<v8::Value>::New(isolate(), web_contents_);
90+
}
91+
92+
// static
93+
void BrowserView::BuildPrototype(v8::Isolate* isolate,
94+
v8::Local<v8::FunctionTemplate> prototype) {
95+
prototype->SetClassName(mate::StringToV8(isolate, "BrowserView"));
96+
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
97+
.MakeDestroyable()
98+
.SetMethod("setBounds", &BrowserView::SetBounds)
99+
.SetMethod("setBackgroundColor", &BrowserView::SetBackgroundColor)
100+
.SetProperty("webContents", &BrowserView::WebContents)
101+
.SetProperty("id", &BrowserView::ID);
102+
}
103+
104+
} // namespace api
105+
106+
} // namespace atom
107+
108+
namespace {
109+
110+
using atom::api::BrowserView;
111+
112+
void Initialize(v8::Local<v8::Object> exports,
113+
v8::Local<v8::Value> unused,
114+
v8::Local<v8::Context> context,
115+
void* priv) {
116+
v8::Isolate* isolate = context->GetIsolate();
117+
BrowserView::SetConstructor(isolate, base::Bind(&BrowserView::New));
118+
119+
mate::Dictionary browser_view(
120+
isolate, BrowserView::GetConstructor(isolate)->GetFunction());
121+
122+
mate::Dictionary dict(isolate, exports);
123+
dict.Set("BrowserView", browser_view);
124+
}
125+
126+
} // namespace
127+
128+
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_browser_view, Initialize)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) 2017 GitHub, Inc.
2+
// Use of this source code is governed by the MIT license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_
6+
#define ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_
7+
8+
#include <memory>
9+
#include <string>
10+
11+
#include "atom/browser/api/trackable_object.h"
12+
#include "native_mate/handle.h"
13+
14+
namespace gfx {
15+
class Rect;
16+
}
17+
18+
namespace mate {
19+
class Arguments;
20+
class Dictionary;
21+
} // namespace mate
22+
23+
namespace atom {
24+
25+
class NativeBrowserView;
26+
27+
namespace api {
28+
29+
class WebContents;
30+
31+
class BrowserView : public mate::TrackableObject<BrowserView> {
32+
public:
33+
static mate::WrappableBase* New(mate::Arguments* args);
34+
35+
static void BuildPrototype(v8::Isolate* isolate,
36+
v8::Local<v8::FunctionTemplate> prototype);
37+
38+
NativeBrowserView* view() const { return view_.get(); }
39+
40+
int32_t ID() const;
41+
42+
protected:
43+
BrowserView(v8::Isolate* isolate,
44+
v8::Local<v8::Object> wrapper,
45+
const mate::Dictionary& options);
46+
~BrowserView() override;
47+
48+
private:
49+
void Init(v8::Isolate* isolate,
50+
v8::Local<v8::Object> wrapper,
51+
const mate::Dictionary& options);
52+
53+
void SetBounds(const gfx::Rect& bounds);
54+
void SetBackgroundColor(const std::string& color_name);
55+
56+
v8::Local<v8::Value> WebContents();
57+
58+
v8::Global<v8::Value> web_contents_;
59+
class WebContents* api_web_contents_;
60+
61+
std::unique_ptr<NativeBrowserView> view_;
62+
63+
DISALLOW_COPY_AND_ASSIGN(BrowserView);
64+
};
65+
66+
} // namespace api
67+
68+
} // namespace atom
69+
70+
#endif // ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_

atom/browser/api/atom_api_web_contents.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ struct Converter<atom::api::WebContents::Type> {
188188
switch (val) {
189189
case Type::BACKGROUND_PAGE: type = "backgroundPage"; break;
190190
case Type::BROWSER_WINDOW: type = "window"; break;
191+
case Type::BROWSER_VIEW: type = "browserView"; break;
191192
case Type::REMOTE: type = "remote"; break;
192193
case Type::WEB_VIEW: type = "webview"; break;
193194
case Type::OFF_SCREEN: type = "offscreen"; break;
@@ -202,10 +203,12 @@ struct Converter<atom::api::WebContents::Type> {
202203
std::string type;
203204
if (!ConvertFromV8(isolate, val, &type))
204205
return false;
205-
if (type == "webview") {
206-
*out = Type::WEB_VIEW;
207-
} else if (type == "backgroundPage") {
206+
if (type == "backgroundPage") {
208207
*out = Type::BACKGROUND_PAGE;
208+
} else if (type == "browserView") {
209+
*out = Type::BROWSER_VIEW;
210+
} else if (type == "webview") {
211+
*out = Type::WEB_VIEW;
209212
} else if (type == "offscreen") {
210213
*out = Type::OFF_SCREEN;
211214
} else {
@@ -306,6 +309,8 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options)
306309
type_ = WEB_VIEW;
307310
else if (options.Get("isBackgroundPage", &b) && b)
308311
type_ = BACKGROUND_PAGE;
312+
else if (options.Get("isBrowserView", &b) && b)
313+
type_ = BROWSER_VIEW;
309314
else if (options.Get("offscreen", &b) && b)
310315
type_ = OFF_SCREEN;
311316

atom/browser/api/atom_api_web_contents.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ class WebContents : public mate::TrackableObject<WebContents>,
5353
public:
5454
enum Type {
5555
BACKGROUND_PAGE, // A DevTools extension background page.
56-
BROWSER_WINDOW, // Used by BrowserWindow.
57-
REMOTE, // Thin wrap around an existing WebContents.
58-
WEB_VIEW, // Used by <webview>.
59-
OFF_SCREEN, // Used for offscreen rendering
56+
BROWSER_WINDOW, // Used by BrowserWindow.
57+
BROWSER_VIEW, // Used by BrowserView.
58+
REMOTE, // Thin wrap around an existing WebContents.
59+
WEB_VIEW, // Used by <webview>.
60+
OFF_SCREEN, // Used for offscreen rendering
6061
};
6162

6263
// For node.js callback function type: function(error, buffer)

atom/browser/api/atom_api_window.cc

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "atom/browser/api/atom_api_window.h"
66
#include "atom/common/native_mate_converters/value_converter.h"
77

8+
#include "atom/browser/api/atom_api_browser_view.h"
89
#include "atom/browser/api/atom_api_menu.h"
910
#include "atom/browser/api/atom_api_web_contents.h"
1011
#include "atom/browser/browser.h"
@@ -816,6 +817,25 @@ std::vector<v8::Local<v8::Object>> Window::GetChildWindows() const {
816817
return child_windows_.Values(isolate());
817818
}
818819

820+
v8::Local<v8::Value> Window::GetBrowserView() const {
821+
if (browser_view_.IsEmpty()) {
822+
return v8::Null(isolate());
823+
}
824+
825+
return v8::Local<v8::Value>::New(isolate(), browser_view_);
826+
}
827+
828+
void Window::SetBrowserView(v8::Local<v8::Value> value) {
829+
mate::Handle<BrowserView> browser_view;
830+
if (value->IsNull()) {
831+
window_->SetBrowserView(nullptr);
832+
browser_view_.Reset();
833+
} else if (mate::ConvertFromV8(isolate(), value, &browser_view)) {
834+
window_->SetBrowserView(browser_view->view());
835+
browser_view_.Reset(isolate(), value);
836+
}
837+
}
838+
819839
bool Window::IsModal() const {
820840
return window_->is_modal();
821841
}
@@ -862,10 +882,11 @@ int32_t Window::ID() const {
862882
}
863883

864884
v8::Local<v8::Value> Window::WebContents(v8::Isolate* isolate) {
865-
if (web_contents_.IsEmpty())
885+
if (web_contents_.IsEmpty()) {
866886
return v8::Null(isolate);
867-
else
868-
return v8::Local<v8::Value>::New(isolate, web_contents_);
887+
}
888+
889+
return v8::Local<v8::Value>::New(isolate, web_contents_);
869890
}
870891

871892
void Window::RemoveFromParentChildWindows() {
@@ -910,6 +931,8 @@ void Window::BuildPrototype(v8::Isolate* isolate,
910931
#endif
911932
.SetMethod("getParentWindow", &Window::GetParentWindow)
912933
.SetMethod("getChildWindows", &Window::GetChildWindows)
934+
.SetMethod("getBrowserView", &Window::GetBrowserView)
935+
.SetMethod("setBrowserView", &Window::SetBrowserView)
913936
.SetMethod("isModal", &Window::IsModal)
914937
.SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
915938
.SetMethod("getBounds", &Window::GetBounds)

atom/browser/api/atom_api_window.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ class Window : public mate::TrackableObject<Window>,
180180
void SetParentWindow(v8::Local<v8::Value> value, mate::Arguments* args);
181181
v8::Local<v8::Value> GetParentWindow() const;
182182
std::vector<v8::Local<v8::Object>> GetChildWindows() const;
183+
v8::Local<v8::Value> GetBrowserView() const;
184+
void SetBrowserView(v8::Local<v8::Value> value);
183185
bool IsModal() const;
184186
v8::Local<v8::Value> GetNativeWindowHandle();
185187

@@ -220,6 +222,7 @@ class Window : public mate::TrackableObject<Window>,
220222
MessageCallbackMap messages_callback_map_;
221223
#endif
222224

225+
v8::Global<v8::Value> browser_view_;
223226
v8::Global<v8::Value> web_contents_;
224227
v8::Global<v8::Value> menu_;
225228
v8::Global<v8::Value> parent_window_;

atom/browser/common_web_contents_delegate.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,14 @@ void CommonWebContentsDelegate::SetOwnerWindow(NativeWindow* owner_window) {
178178

179179
void CommonWebContentsDelegate::SetOwnerWindow(
180180
content::WebContents* web_contents, NativeWindow* owner_window) {
181-
owner_window_ = owner_window->GetWeakPtr();
181+
owner_window_ = owner_window ? owner_window->GetWeakPtr() : nullptr;
182182
NativeWindowRelay* relay = new NativeWindowRelay(owner_window_);
183-
web_contents->SetUserData(relay->key, relay);
183+
if (owner_window) {
184+
web_contents->SetUserData(relay->key, relay);
185+
} else {
186+
web_contents->RemoveUserData(relay->key);
187+
delete relay;
188+
}
184189
}
185190

186191
void CommonWebContentsDelegate::ResetManagedWebContents() {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2017 GitHub, Inc.
2+
// Use of this source code is governed by the MIT license that can be
3+
// found in the LICENSE file.
4+
5+
#include "atom/browser/native_browser_view.h"
6+
7+
#include "atom/browser/api/atom_api_web_contents.h"
8+
#include "brightray/browser/inspectable_web_contents_view.h"
9+
10+
namespace atom {
11+
12+
NativeBrowserView::NativeBrowserView(
13+
brightray::InspectableWebContentsView* web_contents_view)
14+
: web_contents_view_(web_contents_view) {}
15+
16+
NativeBrowserView::~NativeBrowserView() {}
17+
18+
} // namespace atom

0 commit comments

Comments
 (0)