Implement initial, experimental BrowserView API
authorBirunthan Mohanathas <birunthan@mohanathas.com>
Tue, 11 Apr 2017 17:47:30 +0000 (20:47 +0300)
committerBirunthan Mohanathas <birunthan@mohanathas.com>
Wed, 12 Apr 2017 22:27:27 +0000 (01:27 +0300)
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.

25 files changed:
atom/browser/api/atom_api_browser_view.cc [new file with mode: 0644]
atom/browser/api/atom_api_browser_view.h [new file with mode: 0644]
atom/browser/api/atom_api_web_contents.cc
atom/browser/api/atom_api_web_contents.h
atom/browser/api/atom_api_window.cc
atom/browser/api/atom_api_window.h
atom/browser/common_web_contents_delegate.cc
atom/browser/native_browser_view.cc [new file with mode: 0644]
atom/browser/native_browser_view.h [new file with mode: 0644]
atom/browser/native_browser_view_mac.h [new file with mode: 0644]
atom/browser/native_browser_view_mac.mm [new file with mode: 0644]
atom/browser/native_browser_view_views.cc [new file with mode: 0644]
atom/browser/native_browser_view_views.h [new file with mode: 0644]
atom/browser/native_window.h
atom/browser/native_window_mac.h
atom/browser/native_window_mac.mm
atom/browser/native_window_views.cc
atom/browser/native_window_views.h
atom/common/node_bindings.cc
docs/api/browser-view.md [new file with mode: 0644]
docs/api/browser-window.md
filenames.gypi
lib/browser/api/browser-view.js [new file with mode: 0644]
lib/browser/api/module-list.js
spec/api-browser-view-spec.js [new file with mode: 0644]

diff --git a/atom/browser/api/atom_api_browser_view.cc b/atom/browser/api/atom_api_browser_view.cc
new file mode 100644 (file)
index 0000000..67533e6
--- /dev/null
@@ -0,0 +1,128 @@
+// 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 "atom/browser/api/atom_api_browser_view.h"
+
+#include "atom/browser/api/atom_api_web_contents.h"
+#include "atom/browser/browser.h"
+#include "atom/browser/native_browser_view.h"
+#include "atom/common/color_util.h"
+#include "atom/common/native_mate_converters/gfx_converter.h"
+#include "atom/common/native_mate_converters/value_converter.h"
+#include "atom/common/node_includes.h"
+#include "atom/common/options_switches.h"
+#include "native_mate/constructor.h"
+#include "native_mate/dictionary.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace atom {
+
+namespace api {
+
+BrowserView::BrowserView(v8::Isolate* isolate,
+                         v8::Local<v8::Object> wrapper,
+                         const mate::Dictionary& options)
+    : api_web_contents_(nullptr) {
+  Init(isolate, wrapper, options);
+}
+
+void BrowserView::Init(v8::Isolate* isolate,
+                       v8::Local<v8::Object> wrapper,
+                       const mate::Dictionary& options) {
+  mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate);
+  options.Get(options::kWebPreferences, &web_preferences);
+  web_preferences.Set("isBrowserView", true);
+  mate::Handle<class WebContents> web_contents =
+      WebContents::Create(isolate, web_preferences);
+
+  web_contents_.Reset(isolate, web_contents.ToV8());
+  api_web_contents_ = web_contents.get();
+
+  view_.reset(NativeBrowserView::Create(
+      api_web_contents_->managed_web_contents()->GetView()));
+
+  InitWith(isolate, wrapper);
+}
+
+BrowserView::~BrowserView() {
+  api_web_contents_->DestroyWebContents();
+}
+
+// static
+mate::WrappableBase* BrowserView::New(mate::Arguments* args) {
+  if (!Browser::Get()->is_ready()) {
+    args->ThrowError("Cannot create BrowserView before app is ready");
+    return nullptr;
+  }
+
+  if (args->Length() > 1) {
+    args->ThrowError("Too many arguments");
+    return nullptr;
+  }
+
+  mate::Dictionary options;
+  if (!(args->Length() == 1 && args->GetNext(&options))) {
+    options = mate::Dictionary::CreateEmpty(args->isolate());
+  }
+
+  return new BrowserView(args->isolate(), args->GetThis(), options);
+}
+
+int32_t BrowserView::ID() const {
+  return weak_map_id();
+}
+
+void BrowserView::SetBounds(const gfx::Rect& bounds) {
+  view_->SetBounds(bounds);
+}
+
+void BrowserView::SetBackgroundColor(const std::string& color_name) {
+  view_->SetBackgroundColor(ParseHexColor(color_name));
+}
+
+v8::Local<v8::Value> BrowserView::WebContents() {
+  if (web_contents_.IsEmpty()) {
+    return v8::Null(isolate());
+  }
+
+  return v8::Local<v8::Value>::New(isolate(), web_contents_);
+}
+
+// static
+void BrowserView::BuildPrototype(v8::Isolate* isolate,
+                                 v8::Local<v8::FunctionTemplate> prototype) {
+  prototype->SetClassName(mate::StringToV8(isolate, "BrowserView"));
+  mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
+      .MakeDestroyable()
+      .SetMethod("setBounds", &BrowserView::SetBounds)
+      .SetMethod("setBackgroundColor", &BrowserView::SetBackgroundColor)
+      .SetProperty("webContents", &BrowserView::WebContents)
+      .SetProperty("id", &BrowserView::ID);
+}
+
+}  // namespace api
+
+}  // namespace atom
+
+namespace {
+
+using atom::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();
+  BrowserView::SetConstructor(isolate, base::Bind(&BrowserView::New));
+
+  mate::Dictionary browser_view(
+      isolate, BrowserView::GetConstructor(isolate)->GetFunction());
+
+  mate::Dictionary dict(isolate, exports);
+  dict.Set("BrowserView", browser_view);
+}
+
+}  // namespace
+
+NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_browser_view, Initialize)
diff --git a/atom/browser/api/atom_api_browser_view.h b/atom/browser/api/atom_api_browser_view.h
new file mode 100644 (file)
index 0000000..875d842
--- /dev/null
@@ -0,0 +1,70 @@
+// 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 ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_
+#define ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_
+
+#include <memory>
+#include <string>
+
+#include "atom/browser/api/trackable_object.h"
+#include "native_mate/handle.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mate {
+class Arguments;
+class Dictionary;
+}  // namespace mate
+
+namespace atom {
+
+class NativeBrowserView;
+
+namespace api {
+
+class WebContents;
+
+class BrowserView : public mate::TrackableObject<BrowserView> {
+ public:
+  static mate::WrappableBase* New(mate::Arguments* args);
+
+  static void BuildPrototype(v8::Isolate* isolate,
+                             v8::Local<v8::FunctionTemplate> prototype);
+
+  NativeBrowserView* view() const { return view_.get(); }
+
+  int32_t ID() const;
+
+ protected:
+  BrowserView(v8::Isolate* isolate,
+              v8::Local<v8::Object> wrapper,
+              const mate::Dictionary& options);
+  ~BrowserView() override;
+
+ private:
+  void Init(v8::Isolate* isolate,
+            v8::Local<v8::Object> wrapper,
+            const mate::Dictionary& options);
+
+  void SetBounds(const gfx::Rect& bounds);
+  void SetBackgroundColor(const std::string& color_name);
+
+  v8::Local<v8::Value> WebContents();
+
+  v8::Global<v8::Value> web_contents_;
+  class WebContents* api_web_contents_;
+
+  std::unique_ptr<NativeBrowserView> view_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserView);
+};
+
+}  // namespace api
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_
index 9390777..d3e7bae 100644 (file)
@@ -188,6 +188,7 @@ struct Converter<atom::api::WebContents::Type> {
     switch (val) {
       case Type::BACKGROUND_PAGE: type = "backgroundPage"; break;
       case Type::BROWSER_WINDOW: type = "window"; break;
+      case Type::BROWSER_VIEW: type = "browserView"; break;
       case Type::REMOTE: type = "remote"; break;
       case Type::WEB_VIEW: type = "webview"; break;
       case Type::OFF_SCREEN: type = "offscreen"; break;
@@ -202,10 +203,12 @@ struct Converter<atom::api::WebContents::Type> {
     std::string type;
     if (!ConvertFromV8(isolate, val, &type))
       return false;
-    if (type == "webview") {
-      *out = Type::WEB_VIEW;
-    } else if (type == "backgroundPage") {
+    if (type == "backgroundPage") {
       *out = Type::BACKGROUND_PAGE;
+    } else if (type == "browserView") {
+      *out = Type::BROWSER_VIEW;
+    } else if (type == "webview") {
+      *out = Type::WEB_VIEW;
     } else if (type == "offscreen") {
       *out = Type::OFF_SCREEN;
     } else {
@@ -306,6 +309,8 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options)
     type_ = WEB_VIEW;
   else if (options.Get("isBackgroundPage", &b) && b)
     type_ = BACKGROUND_PAGE;
+  else if (options.Get("isBrowserView", &b) && b)
+    type_ = BROWSER_VIEW;
   else if (options.Get("offscreen", &b) && b)
     type_ = OFF_SCREEN;
 
index c289503..1301ed1 100644 (file)
@@ -53,10 +53,11 @@ class WebContents : public mate::TrackableObject<WebContents>,
  public:
   enum Type {
     BACKGROUND_PAGE,  // A DevTools extension background page.
-    BROWSER_WINDOW,  // Used by BrowserWindow.
-    REMOTE,  // Thin wrap around an existing WebContents.
-    WEB_VIEW,  // Used by <webview>.
-    OFF_SCREEN,  // Used for offscreen rendering
+    BROWSER_WINDOW,   // Used by BrowserWindow.
+    BROWSER_VIEW,     // Used by BrowserView.
+    REMOTE,           // Thin wrap around an existing WebContents.
+    WEB_VIEW,         // Used by <webview>.
+    OFF_SCREEN,       // Used for offscreen rendering
   };
 
   // For node.js callback function type: function(error, buffer)
index 9a4ae58..c670762 100644 (file)
@@ -5,6 +5,7 @@
 #include "atom/browser/api/atom_api_window.h"
 #include "atom/common/native_mate_converters/value_converter.h"
 
+#include "atom/browser/api/atom_api_browser_view.h"
 #include "atom/browser/api/atom_api_menu.h"
 #include "atom/browser/api/atom_api_web_contents.h"
 #include "atom/browser/browser.h"
@@ -816,6 +817,25 @@ std::vector<v8::Local<v8::Object>> Window::GetChildWindows() const {
   return child_windows_.Values(isolate());
 }
 
+v8::Local<v8::Value> Window::GetBrowserView() const {
+  if (browser_view_.IsEmpty()) {
+    return v8::Null(isolate());
+  }
+
+  return v8::Local<v8::Value>::New(isolate(), browser_view_);
+}
+
+void Window::SetBrowserView(v8::Local<v8::Value> value) {
+  mate::Handle<BrowserView> browser_view;
+  if (value->IsNull()) {
+    window_->SetBrowserView(nullptr);
+    browser_view_.Reset();
+  } else if (mate::ConvertFromV8(isolate(), value, &browser_view)) {
+    window_->SetBrowserView(browser_view->view());
+    browser_view_.Reset(isolate(), value);
+  }
+}
+
 bool Window::IsModal() const {
   return window_->is_modal();
 }
@@ -862,10 +882,11 @@ int32_t Window::ID() const {
 }
 
 v8::Local<v8::Value> Window::WebContents(v8::Isolate* isolate) {
-  if (web_contents_.IsEmpty())
+  if (web_contents_.IsEmpty()) {
     return v8::Null(isolate);
-  else
-    return v8::Local<v8::Value>::New(isolate, web_contents_);
+  }
+
+  return v8::Local<v8::Value>::New(isolate, web_contents_);
 }
 
 void Window::RemoveFromParentChildWindows() {
@@ -910,6 +931,8 @@ void Window::BuildPrototype(v8::Isolate* isolate,
 #endif
       .SetMethod("getParentWindow", &Window::GetParentWindow)
       .SetMethod("getChildWindows", &Window::GetChildWindows)
+      .SetMethod("getBrowserView", &Window::GetBrowserView)
+      .SetMethod("setBrowserView", &Window::SetBrowserView)
       .SetMethod("isModal", &Window::IsModal)
       .SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
       .SetMethod("getBounds", &Window::GetBounds)
index 24afec3..d464af5 100644 (file)
@@ -180,6 +180,8 @@ class Window : public mate::TrackableObject<Window>,
   void SetParentWindow(v8::Local<v8::Value> value, mate::Arguments* args);
   v8::Local<v8::Value> GetParentWindow() const;
   std::vector<v8::Local<v8::Object>> GetChildWindows() const;
+  v8::Local<v8::Value> GetBrowserView() const;
+  void SetBrowserView(v8::Local<v8::Value> value);
   bool IsModal() const;
   v8::Local<v8::Value> GetNativeWindowHandle();
 
@@ -220,6 +222,7 @@ class Window : public mate::TrackableObject<Window>,
   MessageCallbackMap messages_callback_map_;
 #endif
 
+  v8::Global<v8::Value> browser_view_;
   v8::Global<v8::Value> web_contents_;
   v8::Global<v8::Value> menu_;
   v8::Global<v8::Value> parent_window_;
index f7f9e46..a10f959 100644 (file)
@@ -178,9 +178,14 @@ void CommonWebContentsDelegate::SetOwnerWindow(NativeWindow* owner_window) {
 
 void CommonWebContentsDelegate::SetOwnerWindow(
     content::WebContents* web_contents, NativeWindow* owner_window) {
-  owner_window_ = owner_window->GetWeakPtr();
+  owner_window_ = owner_window ? owner_window->GetWeakPtr() : nullptr;
   NativeWindowRelay* relay = new NativeWindowRelay(owner_window_);
-  web_contents->SetUserData(relay->key, relay);
+  if (owner_window) {
+    web_contents->SetUserData(relay->key, relay);
+  } else {
+    web_contents->RemoveUserData(relay->key);
+    delete relay;
+  }
 }
 
 void CommonWebContentsDelegate::ResetManagedWebContents() {
diff --git a/atom/browser/native_browser_view.cc b/atom/browser/native_browser_view.cc
new file mode 100644 (file)
index 0000000..949e5e9
--- /dev/null
@@ -0,0 +1,18 @@
+// 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 "atom/browser/native_browser_view.h"
+
+#include "atom/browser/api/atom_api_web_contents.h"
+#include "brightray/browser/inspectable_web_contents_view.h"
+
+namespace atom {
+
+NativeBrowserView::NativeBrowserView(
+    brightray::InspectableWebContentsView* web_contents_view)
+    : web_contents_view_(web_contents_view) {}
+
+NativeBrowserView::~NativeBrowserView() {}
+
+}  // namespace atom
diff --git a/atom/browser/native_browser_view.h b/atom/browser/native_browser_view.h
new file mode 100644 (file)
index 0000000..f9af80f
--- /dev/null
@@ -0,0 +1,51 @@
+// 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 ATOM_BROWSER_NATIVE_BROWSER_VIEW_H_
+#define ATOM_BROWSER_NATIVE_BROWSER_VIEW_H_
+
+#include "base/macros.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace brightray {
+class InspectableWebContentsView;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace atom {
+
+namespace api {
+class WebContents;
+}
+
+class NativeBrowserView {
+ public:
+  virtual ~NativeBrowserView();
+
+  static NativeBrowserView* Create(
+      brightray::InspectableWebContentsView* web_contents_view);
+
+  brightray::InspectableWebContentsView* GetInspectableWebContentsView() {
+    return web_contents_view_;
+  }
+
+  virtual void SetBounds(const gfx::Rect& bounds) = 0;
+  virtual void SetBackgroundColor(SkColor color) = 0;
+
+ protected:
+  explicit NativeBrowserView(
+      brightray::InspectableWebContentsView* web_contents_view);
+
+  brightray::InspectableWebContentsView* web_contents_view_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeBrowserView);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_NATIVE_BROWSER_VIEW_H_
diff --git a/atom/browser/native_browser_view_mac.h b/atom/browser/native_browser_view_mac.h
new file mode 100644 (file)
index 0000000..3053a09
--- /dev/null
@@ -0,0 +1,29 @@
+// 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 ATOM_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
+#define ATOM_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "atom/browser/native_browser_view.h"
+
+namespace atom {
+
+class NativeBrowserViewMac : public NativeBrowserView {
+ public:
+  explicit NativeBrowserViewMac(
+      brightray::InspectableWebContentsView* web_contents_view);
+  ~NativeBrowserViewMac() override;
+
+  void SetBounds(const gfx::Rect& bounds) override;
+  void SetBackgroundColor(SkColor color) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeBrowserViewMac);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
diff --git a/atom/browser/native_browser_view_mac.mm b/atom/browser/native_browser_view_mac.mm
new file mode 100644 (file)
index 0000000..73a36cd
--- /dev/null
@@ -0,0 +1,40 @@
+// 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 "atom/browser/native_browser_view_mac.h"
+
+#include "brightray/browser/inspectable_web_contents_view.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace atom {
+
+NativeBrowserViewMac::NativeBrowserViewMac(
+    brightray::InspectableWebContentsView* web_contents_view)
+    : NativeBrowserView(web_contents_view) {}
+
+NativeBrowserViewMac::~NativeBrowserViewMac() {}
+
+void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) {
+  auto* view = GetInspectableWebContentsView()->GetNativeView();
+  auto* superview = view.superview;
+  const auto superview_height = superview ? superview.frame.size.height : 0;
+  view.frame =
+      NSMakeRect(bounds.x(), superview_height - bounds.y() - bounds.height(),
+                 bounds.width(), bounds.height());
+}
+
+void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
+  auto* view = GetInspectableWebContentsView()->GetNativeView();
+  view.wantsLayer = YES;
+  view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color);
+}
+
+// static
+NativeBrowserView* NativeBrowserView::Create(
+    brightray::InspectableWebContentsView* web_contents_view) {
+  return new NativeBrowserViewMac(web_contents_view);
+}
+
+}  // namespace atom
diff --git a/atom/browser/native_browser_view_views.cc b/atom/browser/native_browser_view_views.cc
new file mode 100644 (file)
index 0000000..08a8123
--- /dev/null
@@ -0,0 +1,36 @@
+// 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 "atom/browser/native_browser_view_views.h"
+
+#include "brightray/browser/inspectable_web_contents_view.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/background.h"
+#include "ui/views/view.h"
+
+namespace atom {
+
+NativeBrowserViewViews::NativeBrowserViewViews(
+    brightray::InspectableWebContentsView* web_contents_view)
+    : NativeBrowserView(web_contents_view) {}
+
+NativeBrowserViewViews::~NativeBrowserViewViews() {}
+
+void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
+  auto* view = GetInspectableWebContentsView()->GetView();
+  view->SetBoundsRect(bounds);
+}
+
+void NativeBrowserViewViews::SetBackgroundColor(SkColor color) {
+  auto* view = GetInspectableWebContentsView()->GetView();
+  view->set_background(views::Background::CreateSolidBackground(color));
+}
+
+// static
+NativeBrowserView* NativeBrowserView::Create(
+    brightray::InspectableWebContentsView* web_contents_view) {
+  return new NativeBrowserViewViews(web_contents_view);
+}
+
+}  // namespace atom
diff --git a/atom/browser/native_browser_view_views.h b/atom/browser/native_browser_view_views.h
new file mode 100644 (file)
index 0000000..ecfc698
--- /dev/null
@@ -0,0 +1,27 @@
+// 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 ATOM_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
+#define ATOM_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
+
+#include "atom/browser/native_browser_view.h"
+
+namespace atom {
+
+class NativeBrowserViewViews : public NativeBrowserView {
+ public:
+  explicit NativeBrowserViewViews(
+      brightray::InspectableWebContentsView* web_contents_view);
+  ~NativeBrowserViewViews() override;
+
+  void SetBounds(const gfx::Rect& bounds) override;
+  void SetBackgroundColor(SkColor color) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeBrowserViewViews);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
index 227e28c..56702da 100644 (file)
@@ -47,6 +47,8 @@ class Dictionary;
 
 namespace atom {
 
+class NativeBrowserView;
+
 struct DraggableRegion;
 
 class NativeWindow : public base::SupportsUserData,
@@ -144,6 +146,7 @@ class NativeWindow : public base::SupportsUserData,
   virtual void SetFocusable(bool focusable);
   virtual void SetMenu(AtomMenuModel* menu);
   virtual void SetParentWindow(NativeWindow* parent);
+  virtual void SetBrowserView(NativeBrowserView* browser_view) = 0;
   virtual gfx::NativeWindow GetNativeWindow() = 0;
   virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
 
index a535cdb..af0f157 100644 (file)
@@ -87,6 +87,7 @@ class NativeWindowMac : public NativeWindow,
   bool IsDocumentEdited() override;
   void SetIgnoreMouseEvents(bool ignore) override;
   void SetContentProtection(bool enable) override;
+  void SetBrowserView(NativeBrowserView* browser_view) override;
   void SetParentWindow(NativeWindow* parent) override;
   gfx::NativeWindow GetNativeWindow() override;
   gfx::AcceleratedWidget GetAcceleratedWidget() override;
@@ -164,6 +165,8 @@ class NativeWindowMac : public NativeWindow,
   // The view that will fill the whole frameless window.
   base::scoped_nsobject<FullSizeContentView> content_view_;
 
+  NativeBrowserView* browser_view_;
+
   std::vector<DraggableRegion> draggable_regions_;
 
   bool is_kiosk_;
index c3ca24a..b695f8e 100644 (file)
@@ -7,6 +7,7 @@
 #include <Quartz/Quartz.h>
 #include <string>
 
+#include "atom/browser/native_browser_view_mac.h"
 #include "atom/browser/ui/cocoa/atom_touch_bar.h"
 #include "atom/browser/window_list.h"
 #include "atom/common/color_util.h"
@@ -19,9 +20,9 @@
 #include "brightray/browser/inspectable_web_contents_view.h"
 #include "brightray/browser/mac/event_dispatching_window.h"
 #include "content/public/browser/browser_accessibility_state.h"
-#include "content/public/browser/web_contents.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
 #include "native_mate/dictionary.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "third_party/skia/include/core/SkRegion.h"
@@ -671,6 +672,7 @@ NativeWindowMac::NativeWindowMac(
     const mate::Dictionary& options,
     NativeWindow* parent)
     : NativeWindow(web_contents, options, parent),
+      browser_view_(nullptr),
       is_kiosk_(false),
       was_fullscreen_(false),
       zoom_to_page_width_(false),
@@ -1269,6 +1271,26 @@ void NativeWindowMac::SetContentProtection(bool enable) {
                                  : NSWindowSharingReadOnly];
 }
 
+void NativeWindowMac::SetBrowserView(NativeBrowserView* browser_view) {
+  if (browser_view_) {
+    [browser_view_->GetInspectableWebContentsView()->GetNativeView()
+            removeFromSuperview];
+    browser_view_ = nullptr;
+  }
+
+  if (!browser_view) {
+    return;
+  }
+
+  browser_view_ = browser_view;
+  auto* native_view =
+      browser_view->GetInspectableWebContentsView()->GetNativeView();
+  [[window_ contentView] addSubview:native_view
+                         positioned:NSWindowAbove
+                         relativeTo:nil];
+  native_view.hidden = NO;
+}
+
 void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
   if (is_modal())
     return;
index b0722cb..68e210a 100644 (file)
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include "atom/browser/native_browser_view.h"
 #include "atom/browser/ui/views/menu_bar.h"
 #include "atom/browser/ui/views/menu_layout.h"
 #include "atom/browser/window_list.h"
@@ -135,6 +136,7 @@ NativeWindowViews::NativeWindowViews(
     : NativeWindow(web_contents, options, parent),
       window_(new views::Widget),
       web_view_(inspectable_web_contents()->GetView()->GetView()),
+      browser_view_(nullptr),
       menu_bar_autohide_(false),
       menu_bar_visible_(false),
       menu_bar_alt_pressed_(false),
@@ -881,6 +883,20 @@ void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) {
   Layout();
 }
 
+void NativeWindowViews::SetBrowserView(NativeBrowserView* browser_view) {
+  if (browser_view_) {
+    RemoveChildView(browser_view_->GetInspectableWebContentsView()->GetView());
+    browser_view_ = nullptr;
+  }
+
+  if (!browser_view) {
+    return;
+  }
+
+  browser_view_ = browser_view;
+  AddChildView(browser_view->GetInspectableWebContentsView()->GetView());
+}
+
 void NativeWindowViews::SetParentWindow(NativeWindow* parent) {
   NativeWindow::SetParentWindow(parent);
 
index a7f02fb..cf605a0 100644 (file)
@@ -104,6 +104,7 @@ class NativeWindowViews : public NativeWindow,
   void SetContentProtection(bool enable) override;
   void SetFocusable(bool focusable) override;
   void SetMenu(AtomMenuModel* menu_model) override;
+  void SetBrowserView(NativeBrowserView* browser_view) override;
   void SetParentWindow(NativeWindow* parent) override;
   gfx::NativeWindow GetNativeWindow() override;
   void SetOverlayIcon(const gfx::Image& overlay,
@@ -189,6 +190,8 @@ class NativeWindowViews : public NativeWindow,
   std::unique_ptr<views::Widget> window_;
   views::View* web_view_;  // Managed by inspectable_web_contents_.
 
+  NativeBrowserView* browser_view_;
+
   std::unique_ptr<MenuBar> menu_bar_;
   bool menu_bar_autohide_;
   bool menu_bar_visible_;
index f469677..ae757b1 100644 (file)
 // Electron's builtin modules.
 REFERENCE_MODULE(atom_browser_app);
 REFERENCE_MODULE(atom_browser_auto_updater);
+REFERENCE_MODULE(atom_browser_browser_view);
 REFERENCE_MODULE(atom_browser_content_tracing);
-REFERENCE_MODULE(atom_browser_dialog);
 REFERENCE_MODULE(atom_browser_debugger);
 REFERENCE_MODULE(atom_browser_desktop_capturer);
+REFERENCE_MODULE(atom_browser_dialog);
 REFERENCE_MODULE(atom_browser_download_item);
+REFERENCE_MODULE(atom_browser_global_shortcut);
 REFERENCE_MODULE(atom_browser_menu);
 REFERENCE_MODULE(atom_browser_net);
 REFERENCE_MODULE(atom_browser_power_monitor);
 REFERENCE_MODULE(atom_browser_power_save_blocker);
 REFERENCE_MODULE(atom_browser_protocol);
-REFERENCE_MODULE(atom_browser_global_shortcut);
 REFERENCE_MODULE(atom_browser_render_process_preferences);
 REFERENCE_MODULE(atom_browser_session);
 REFERENCE_MODULE(atom_browser_system_preferences);
diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md
new file mode 100644 (file)
index 0000000..1fa518f
--- /dev/null
@@ -0,0 +1,62 @@
+## Class: BrowserView
+
+> Create and control views.
+
+**Note:** The BrowserView API is currently experimental and may change or be
+removed in future Electron releases.
+
+Process: [Main](../glossary.md#main-process)
+
+A `BrowserView` can be used to embed additional web content into a
+`BrowserWindow`. It is like a child window, except that it is positioned
+relative to its owning window. It is meant to be an alternative to the
+`webview` tag.
+
+## Example
+
+```javascript
+// In the main process.
+const {BrowserView, BrowserWindow} = require('electron')
+
+let win = new BrowserWindow({width: 800, height: 600})
+win.on('closed', () => {
+  win = null
+})
+
+let view = new BrowserView()
+win.addChildView(view)
+view.setBounds(0, 0, 300, 300)
+view.webContents.loadURL('https://electron.atom.io')
+```
+
+### `new BrowserView([options])` _Experimental_
+
+* `options` Object (optional)
+  * `webPreferences` Object (optional) - See [BrowserWindow](browser-window.md).
+
+### Instance Properties
+
+Objects created with `new BrowserView` have the following properties:
+
+#### `view.webContents` _Experimental_
+
+A [`webContents`](web-contents.md) object owned by this view.
+
+#### `win.id` _Experimental_
+
+A `Integer` representing the unique ID of the view.
+
+### Instance Methods
+
+Objects created with `new BrowserWindow` have the following instance methods:
+
+#### `win.setBounds(bounds)` _Experimental_
+
+* `bounds` [Rectangle](structures/rectangle.md)
+
+Resizes and moves the view to the supplied bounds relative to the window.
+
+#### `win.setBackgroundColor(color)` _Experimental_
+
+* `color` String - Color in `#aarrggbb` or `#argb` form. The alpha channel is
+  optional.
index ee46a3f..2c720fd 100644 (file)
@@ -1289,6 +1289,13 @@ machine has a touch bar and is running on macOS 10.12.1+.
 **Note:** The TouchBar API is currently experimental and may change or be
 removed in future Electron releases.
 
+#### `win.setBrowserView(browserView)` _Experimental_
+
+* `browserView` [BrowserView](browser-view.md)
+
+**Note:** The BrowserView API is currently experimental and may change or be
+removed in future Electron releases.
+
 [blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5?l=62
 [quick-look]: https://en.wikipedia.org/wiki/Quick_Look
 [vibrancy-docs]: https://developer.apple.com/reference/appkit/nsvisualeffectview?language=objc
index ec9c682..f9fa422 100644 (file)
       'lib/browser/api/auto-updater/auto-updater-native.js',
       'lib/browser/api/auto-updater/auto-updater-win.js',
       'lib/browser/api/auto-updater/squirrel-update-win.js',
+      'lib/browser/api/browser-view.js',
       'lib/browser/api/browser-window.js',
       'lib/browser/api/content-tracing.js',
       'lib/browser/api/dialog.js',
       'lib/browser/api/exports/electron.js',
       'lib/browser/api/global-shortcut.js',
       'lib/browser/api/ipc-main.js',
-      'lib/browser/api/menu.js',
-      'lib/browser/api/menu-item.js',
       'lib/browser/api/menu-item-roles.js',
+      'lib/browser/api/menu-item.js',
+      'lib/browser/api/menu.js',
       'lib/browser/api/module-list.js',
       'lib/browser/api/navigation-controller.js',
       'lib/browser/api/net.js',
       'lib/browser/api/power-monitor.js',
       'lib/browser/api/power-save-blocker.js',
       'lib/browser/api/protocol.js',
-      'lib/browser/api/session.js',
       'lib/browser/api/screen.js',
+      'lib/browser/api/session.js',
       'lib/browser/api/system-preferences.js',
       'lib/browser/api/touch-bar.js',
       'lib/browser/api/tray.js',
       'atom/browser/api/atom_api_app.h',
       'atom/browser/api/atom_api_auto_updater.cc',
       'atom/browser/api/atom_api_auto_updater.h',
+      'atom/browser/api/atom_api_browser_view.cc',
+      'atom/browser/api/atom_api_browser_view.h',
       'atom/browser/api/atom_api_content_tracing.cc',
       'atom/browser/api/atom_api_cookies.cc',
       'atom/browser/api/atom_api_cookies.h',
       'atom/browser/api/atom_api_debugger.h',
       'atom/browser/api/atom_api_desktop_capturer.cc',
       'atom/browser/api/atom_api_desktop_capturer.h',
+      'atom/browser/api/atom_api_dialog.cc',
       'atom/browser/api/atom_api_download_item.cc',
       'atom/browser/api/atom_api_download_item.h',
-      'atom/browser/api/atom_api_dialog.cc',
       'atom/browser/api/atom_api_global_shortcut.cc',
       'atom/browser/api/atom_api_global_shortcut.h',
       'atom/browser/api/atom_api_menu.cc',
       'atom/browser/api/atom_api_menu.h',
-      'atom/browser/api/atom_api_menu_views.cc',
-      'atom/browser/api/atom_api_menu_views.h',
       'atom/browser/api/atom_api_menu_mac.h',
       'atom/browser/api/atom_api_menu_mac.mm',
+      'atom/browser/api/atom_api_menu_views.cc',
+      'atom/browser/api/atom_api_menu_views.h',
       'atom/browser/api/atom_api_net.cc',
       'atom/browser/api/atom_api_net.h',
       'atom/browser/api/atom_api_power_monitor.cc',
       'atom/browser/api/atom_api_power_monitor.h',
       'atom/browser/api/atom_api_power_save_blocker.cc',
       'atom/browser/api/atom_api_power_save_blocker.h',
-      'atom/browser/api/atom_api_render_process_preferences.cc',
-      'atom/browser/api/atom_api_render_process_preferences.h',
       'atom/browser/api/atom_api_protocol.cc',
       'atom/browser/api/atom_api_protocol.h',
+      'atom/browser/api/atom_api_render_process_preferences.cc',
+      'atom/browser/api/atom_api_render_process_preferences.h',
       'atom/browser/api/atom_api_screen.cc',
       'atom/browser/api/atom_api_screen.h',
       'atom/browser/api/atom_api_session.cc',
       'atom/browser/mac/atom_application_delegate.mm',
       'atom/browser/mac/dict_util.h',
       'atom/browser/mac/dict_util.mm',
+      'atom/browser/native_browser_view.cc',
+      'atom/browser/native_browser_view.h',
+      'atom/browser/native_browser_view_mac.h',
+      'atom/browser/native_browser_view_mac.mm',
+      'atom/browser/native_browser_view_views.h',
+      'atom/browser/native_browser_view_views.cc',
       'atom/browser/native_window.cc',
       'atom/browser/native_window.h',
       'atom/browser/native_window_views_win.cc',
diff --git a/lib/browser/api/browser-view.js b/lib/browser/api/browser-view.js
new file mode 100644 (file)
index 0000000..60023fe
--- /dev/null
@@ -0,0 +1,8 @@
+'use strict'
+
+const {EventEmitter} = require('events')
+const {BrowserView} = process.atomBinding('browser_view')
+
+Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
+
+module.exports = BrowserView
index 3274f0b..64b2829 100644 (file)
@@ -2,6 +2,7 @@
 module.exports = [
   {name: 'app', file: 'app'},
   {name: 'autoUpdater', file: 'auto-updater'},
+  {name: 'BrowserView', file: 'browser-view'},
   {name: 'BrowserWindow', file: 'browser-window'},
   {name: 'contentTracing', file: 'content-tracing'},
   {name: 'dialog', file: 'dialog'},
diff --git a/spec/api-browser-view-spec.js b/spec/api-browser-view-spec.js
new file mode 100644 (file)
index 0000000..d4ab02a
--- /dev/null
@@ -0,0 +1,71 @@
+'use strict'
+
+const assert = require('assert')
+const {closeWindow} = require('./window-helpers')
+
+const {remote} = require('electron')
+const {BrowserView, BrowserWindow} = remote
+
+describe('View module', function () {
+  var w = null
+
+  beforeEach(function () {
+    w = new BrowserWindow({
+      show: false,
+      width: 400,
+      height: 400,
+      webPreferences: {
+        backgroundThrottling: false
+      }
+    })
+  })
+
+  afterEach(function () {
+    return closeWindow(w).then(function () { w = null })
+  })
+
+  describe('BrowserView.setBackgroundColor()', function () {
+    it('does not throw for valid args', function () {
+      const view = new BrowserView()
+      view.setBackgroundColor('#000')
+    })
+
+    it('throws for invalid args', function () {
+      const view = new BrowserView()
+      assert.throws(function () {
+        view.setBackgroundColor(null)
+      }, /conversion failure/)
+    })
+  })
+
+  describe('BrowserView.setBounds()', function () {
+    it('does not throw for valid args', function () {
+      const view = new BrowserView()
+      view.setBounds({ x: 0, y: 0, width: 1, height: 1 })
+    })
+
+    it('throws for invalid args', function () {
+      const view = new BrowserView()
+      assert.throws(function () {
+        view.setBounds(null)
+      }, /conversion failure/)
+      assert.throws(function () {
+        view.setBounds({})
+      }, /conversion failure/)
+    })
+  })
+
+  describe('BrowserWindow.setBrowserView()', function () {
+    it('does not throw for valid args', function () {
+      const view = new BrowserView()
+      w.setBrowserView(view)
+    })
+
+    it('does not throw if called multiple times with same view', function () {
+      const view = new BrowserView()
+      w.setBrowserView(view)
+      w.setBrowserView(view)
+      w.setBrowserView(view)
+    })
+  })
+})