From 8b9f7e5b00187a1fc608c883fdfdbae9727f1729 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Tue, 11 Apr 2017 20:47:30 +0300 Subject: [PATCH 1/5] Implement initial, experimental BrowserView API Right now, `` is the only way to embed additional content in a `BrowserWindow`. Unfortunately `` 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 `` through the OOPIF work, it is probable that there will continue to be ``-specific bugs in the future. Therefore, this introduces a `` 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 ``, 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. --- atom/browser/api/atom_api_browser_view.cc | 128 +++++++++++++++++++ atom/browser/api/atom_api_browser_view.h | 70 ++++++++++ atom/browser/api/atom_api_web_contents.cc | 11 +- atom/browser/api/atom_api_web_contents.h | 9 +- atom/browser/api/atom_api_window.cc | 29 ++++- atom/browser/api/atom_api_window.h | 3 + atom/browser/common_web_contents_delegate.cc | 9 +- atom/browser/native_browser_view.cc | 18 +++ atom/browser/native_browser_view.h | 51 ++++++++ atom/browser/native_browser_view_mac.h | 29 +++++ atom/browser/native_browser_view_mac.mm | 40 ++++++ atom/browser/native_browser_view_views.cc | 36 ++++++ atom/browser/native_browser_view_views.h | 27 ++++ atom/browser/native_window.h | 3 + atom/browser/native_window_mac.h | 3 + atom/browser/native_window_mac.mm | 24 +++- atom/browser/native_window_views.cc | 16 +++ atom/browser/native_window_views.h | 3 + atom/common/node_bindings.cc | 5 +- docs/api/browser-view.md | 62 +++++++++ docs/api/browser-window.md | 7 + filenames.gypi | 25 ++-- lib/browser/api/browser-view.js | 8 ++ lib/browser/api/module-list.js | 1 + spec/api-browser-view-spec.js | 71 ++++++++++ 25 files changed, 665 insertions(+), 23 deletions(-) create mode 100644 atom/browser/api/atom_api_browser_view.cc create mode 100644 atom/browser/api/atom_api_browser_view.h create mode 100644 atom/browser/native_browser_view.cc create mode 100644 atom/browser/native_browser_view.h create mode 100644 atom/browser/native_browser_view_mac.h create mode 100644 atom/browser/native_browser_view_mac.mm create mode 100644 atom/browser/native_browser_view_views.cc create mode 100644 atom/browser/native_browser_view_views.h create mode 100644 docs/api/browser-view.md create mode 100644 lib/browser/api/browser-view.js create mode 100644 spec/api-browser-view-spec.js diff --git a/atom/browser/api/atom_api_browser_view.cc b/atom/browser/api/atom_api_browser_view.cc new file mode 100644 index 0000000000..67533e645d --- /dev/null +++ b/atom/browser/api/atom_api_browser_view.cc @@ -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 wrapper, + const mate::Dictionary& options) + : api_web_contents_(nullptr) { + Init(isolate, wrapper, options); +} + +void BrowserView::Init(v8::Isolate* isolate, + v8::Local 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 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 BrowserView::WebContents() { + if (web_contents_.IsEmpty()) { + return v8::Null(isolate()); + } + + return v8::Local::New(isolate(), web_contents_); +} + +// static +void BrowserView::BuildPrototype(v8::Isolate* isolate, + v8::Local 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 exports, + v8::Local unused, + v8::Local 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 index 0000000000..875d842898 --- /dev/null +++ b/atom/browser/api/atom_api_browser_view.h @@ -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 +#include + +#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 { + public: + static mate::WrappableBase* New(mate::Arguments* args); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + NativeBrowserView* view() const { return view_.get(); } + + int32_t ID() const; + + protected: + BrowserView(v8::Isolate* isolate, + v8::Local wrapper, + const mate::Dictionary& options); + ~BrowserView() override; + + private: + void Init(v8::Isolate* isolate, + v8::Local wrapper, + const mate::Dictionary& options); + + void SetBounds(const gfx::Rect& bounds); + void SetBackgroundColor(const std::string& color_name); + + v8::Local WebContents(); + + v8::Global web_contents_; + class WebContents* api_web_contents_; + + std::unique_ptr view_; + + DISALLOW_COPY_AND_ASSIGN(BrowserView); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_BROWSER_VIEW_H_ diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 9390777f9c..d3e7bae2e4 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -188,6 +188,7 @@ struct Converter { 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 { 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; diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index c289503b43..1301ed15f7 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -53,10 +53,11 @@ class WebContents : public mate::TrackableObject, 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 . - 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 . + OFF_SCREEN, // Used for offscreen rendering }; // For node.js callback function type: function(error, buffer) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 9a4ae5850d..c670762491 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -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> Window::GetChildWindows() const { return child_windows_.Values(isolate()); } +v8::Local Window::GetBrowserView() const { + if (browser_view_.IsEmpty()) { + return v8::Null(isolate()); + } + + return v8::Local::New(isolate(), browser_view_); +} + +void Window::SetBrowserView(v8::Local value) { + mate::Handle 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 Window::WebContents(v8::Isolate* isolate) { - if (web_contents_.IsEmpty()) + if (web_contents_.IsEmpty()) { return v8::Null(isolate); - else - return v8::Local::New(isolate, web_contents_); + } + + return v8::Local::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) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 24afec354d..d464af58ea 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -180,6 +180,8 @@ class Window : public mate::TrackableObject, void SetParentWindow(v8::Local value, mate::Arguments* args); v8::Local GetParentWindow() const; std::vector> GetChildWindows() const; + v8::Local GetBrowserView() const; + void SetBrowserView(v8::Local value); bool IsModal() const; v8::Local GetNativeWindowHandle(); @@ -220,6 +222,7 @@ class Window : public mate::TrackableObject, MessageCallbackMap messages_callback_map_; #endif + v8::Global browser_view_; v8::Global web_contents_; v8::Global menu_; v8::Global parent_window_; diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index f7f9e46250..a10f959dce 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -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 index 0000000000..949e5e9ec9 --- /dev/null +++ b/atom/browser/native_browser_view.cc @@ -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 index 0000000000..f9af80f65e --- /dev/null +++ b/atom/browser/native_browser_view.h @@ -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 index 0000000000..3053a098fc --- /dev/null +++ b/atom/browser/native_browser_view_mac.h @@ -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 + +#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 index 0000000000..73a36cd347 --- /dev/null +++ b/atom/browser/native_browser_view_mac.mm @@ -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 index 0000000000..08a8123bca --- /dev/null +++ b/atom/browser/native_browser_view_views.cc @@ -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 index 0000000000..ecfc6989df --- /dev/null +++ b/atom/browser/native_browser_view_views.h @@ -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_ diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 227e28c1e4..56702daef6 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -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; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index a535cdb4be..af0f157eca 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -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 content_view_; + NativeBrowserView* browser_view_; + std::vector draggable_regions_; bool is_kiosk_; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index c3ca24a3d4..b695f8eafa 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -7,6 +7,7 @@ #include #include +#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; diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index b0722cbc4a..68e210a625 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -7,6 +7,7 @@ #include #include +#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); diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index a7f02fb272..cf605a0231 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -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 window_; views::View* web_view_; // Managed by inspectable_web_contents_. + NativeBrowserView* browser_view_; + std::unique_ptr menu_bar_; bool menu_bar_autohide_; bool menu_bar_visible_; diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index f4696773da..ae757b1da8 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -33,17 +33,18 @@ // 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 index 0000000000..1fa518fdd7 --- /dev/null +++ b/docs/api/browser-view.md @@ -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. diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index ee46a3fa03..2c720fdfac 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -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 diff --git a/filenames.gypi b/filenames.gypi index ec9c68241c..f9fa4227ea 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -13,23 +13,24 @@ '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', @@ -103,6 +104,8 @@ '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', @@ -110,27 +113,27 @@ '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', @@ -216,6 +219,12 @@ '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 index 0000000000..60023fef92 --- /dev/null +++ b/lib/browser/api/browser-view.js @@ -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 diff --git a/lib/browser/api/module-list.js b/lib/browser/api/module-list.js index 3274f0b6d4..64b2829064 100644 --- a/lib/browser/api/module-list.js +++ b/lib/browser/api/module-list.js @@ -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 index 0000000000..d4ab02a417 --- /dev/null +++ b/spec/api-browser-view-spec.js @@ -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) + }) + }) +}) From 638eae10807f41013b4efbf58f1706786ea7000c Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 12 Apr 2017 14:00:00 +0300 Subject: [PATCH 2/5] Remove MenuLayout in favor of NativeWindowViews::Layout --- atom/browser/native_window_views.cc | 43 +++++++++++-- atom/browser/native_window_views.h | 1 + atom/browser/ui/views/menu_layout.cc | 91 ---------------------------- atom/browser/ui/views/menu_layout.h | 36 ----------- filenames.gypi | 2 - 5 files changed, 40 insertions(+), 133 deletions(-) delete mode 100644 atom/browser/ui/views/menu_layout.cc delete mode 100644 atom/browser/ui/views/menu_layout.h diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 68e210a625..8f5072931b 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -9,7 +9,6 @@ #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" #include "atom/common/color_util.h" #include "atom/common/draggable_region.h" @@ -71,6 +70,20 @@ const int kMenuBarHeight = 25; #endif #if defined(OS_WIN) +gfx::Rect SubtractBorderSize(gfx::Rect bounds) { + gfx::Point borderSize = gfx::Point( + GetSystemMetrics(SM_CXSIZEFRAME) - 1, // width + GetSystemMetrics(SM_CYSIZEFRAME) - 1); // height + gfx::Point dpiAdjustedSize = + display::win::ScreenWin::ScreenToDIPPoint(borderSize); + + bounds.set_x(bounds.x() + dpiAdjustedSize.x()); + bounds.set_y(bounds.y() + dpiAdjustedSize.y()); + bounds.set_width(bounds.width() - 2 * dpiAdjustedSize.x()); + bounds.set_height(bounds.height() - 2 * dpiAdjustedSize.y()); + return bounds; +} + void FlipWindowStyle(HWND handle, bool on, DWORD flag) { DWORD style = ::GetWindowLong(handle, GWL_STYLE); if (on) @@ -276,9 +289,6 @@ NativeWindowViews::NativeWindowViews( SetWindowType(GetAcceleratedWidget(), window_type); #endif - // Add web view. - SetLayoutManager(new MenuLayout(this, kMenuBarHeight)); - AddChildView(web_view_); #if defined(OS_WIN) @@ -1264,6 +1274,31 @@ void NativeWindowViews::HandleKeyboardEvent( } } +void NativeWindowViews::Layout() { +#if defined(OS_WIN) + // Reserve border space for maximized frameless window so we won't have the + // content go outside of screen. + if (!has_frame() && IsMaximized()) { + gfx::Rect bounds = SubtractBorderSize(GetContentsBounds()); + web_view_->SetBoundsRect(bounds); + return; + } +#endif + + const auto size = GetContentsBounds().size(); + const auto menu_bar_bounds = + menu_bar_ ? gfx::Rect(0, 0, size.width(), kMenuBarHeight) : gfx::Rect(); + if (menu_bar_) { + menu_bar_->SetBoundsRect(menu_bar_bounds); + } + + if (web_view_) { + web_view_->SetBoundsRect( + gfx::Rect(0, menu_bar_bounds.height(), size.width(), + size.height() - menu_bar_bounds.height())); + } +} + gfx::Size NativeWindowViews::GetMinimumSize() { return NativeWindow::GetMinimumSize(); } diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index cf605a0231..276cd4adde 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -177,6 +177,7 @@ class NativeWindowViews : public NativeWindow, const content::NativeWebKeyboardEvent& event) override; // views::View: + void Layout() override; gfx::Size GetMinimumSize() override; gfx::Size GetMaximumSize() override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; diff --git a/atom/browser/ui/views/menu_layout.cc b/atom/browser/ui/views/menu_layout.cc deleted file mode 100644 index d70a4655a1..0000000000 --- a/atom/browser/ui/views/menu_layout.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/views/menu_layout.h" - -#if defined(OS_WIN) -#include "atom/browser/native_window_views.h" -#include "ui/display/win/screen_win.h" -#endif - -namespace atom { - -namespace { - -#if defined(OS_WIN) -gfx::Rect SubtractBorderSize(gfx::Rect bounds) { - gfx::Point borderSize = gfx::Point( - GetSystemMetrics(SM_CXSIZEFRAME) - 1, // width - GetSystemMetrics(SM_CYSIZEFRAME) - 1); // height - gfx::Point dpiAdjustedSize = - display::win::ScreenWin::ScreenToDIPPoint(borderSize); - - bounds.set_x(bounds.x() + dpiAdjustedSize.x()); - bounds.set_y(bounds.y() + dpiAdjustedSize.y()); - bounds.set_width(bounds.width() - 2 * dpiAdjustedSize.x()); - bounds.set_height(bounds.height() - 2 * dpiAdjustedSize.y()); - return bounds; -} -#endif - -} // namespace - -MenuLayout::MenuLayout(NativeWindowViews* window, int menu_height) - : window_(window), - menu_height_(menu_height) { -} - -MenuLayout::~MenuLayout() { -} - -void MenuLayout::Layout(views::View* host) { -#if defined(OS_WIN) - // Reserve border space for maximized frameless window so we won't have the - // content go outside of screen. - if (!window_->has_frame() && window_->IsMaximized()) { - gfx::Rect bounds = SubtractBorderSize(host->GetContentsBounds()); - host->child_at(0)->SetBoundsRect(bounds); - return; - } -#endif - - if (!HasMenu(host)) { - views::FillLayout::Layout(host); - return; - } - - gfx::Size size = host->GetContentsBounds().size(); - gfx::Rect menu_Bar_bounds = gfx::Rect(0, 0, size.width(), menu_height_); - gfx::Rect web_view_bounds = gfx::Rect( - 0, menu_height_, size.width(), size.height() - menu_height_); - - views::View* web_view = host->child_at(0); - views::View* menu_bar = host->child_at(1); - web_view->SetBoundsRect(web_view_bounds); - menu_bar->SetBoundsRect(menu_Bar_bounds); -} - -gfx::Size MenuLayout::GetPreferredSize(const views::View* host) const { - gfx::Size size = views::FillLayout::GetPreferredSize(host); - if (!HasMenu(host)) - return size; - - size.set_height(size.height() + menu_height_); - return size; -} - -int MenuLayout::GetPreferredHeightForWidth( - const views::View* host, int width) const { - int height = views::FillLayout::GetPreferredHeightForWidth(host, width); - if (!HasMenu(host)) - return height; - - return height + menu_height_; -} - -bool MenuLayout::HasMenu(const views::View* host) const { - return host->child_count() == 2; -} - -} // namespace atom diff --git a/atom/browser/ui/views/menu_layout.h b/atom/browser/ui/views/menu_layout.h deleted file mode 100644 index 0a8464a1d4..0000000000 --- a/atom/browser/ui/views/menu_layout.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_UI_VIEWS_MENU_LAYOUT_H_ -#define ATOM_BROWSER_UI_VIEWS_MENU_LAYOUT_H_ - -#include "ui/views/layout/fill_layout.h" - -namespace atom { - -class NativeWindowViews; - -class MenuLayout : public views::FillLayout { - public: - MenuLayout(NativeWindowViews* window, int menu_height); - virtual ~MenuLayout(); - - // views::LayoutManager: - void Layout(views::View* host) override; - gfx::Size GetPreferredSize(const views::View* host) const override; - int GetPreferredHeightForWidth( - const views::View* host, int width) const override; - - private: - bool HasMenu(const views::View* host) const; - - NativeWindowViews* window_; - int menu_height_; - - DISALLOW_COPY_AND_ASSIGN(MenuLayout); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_UI_VIEWS_MENU_LAYOUT_H_ diff --git a/filenames.gypi b/filenames.gypi index f9fa4227ea..8f14b29883 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -322,8 +322,6 @@ 'atom/browser/ui/views/menu_bar.h', 'atom/browser/ui/views/menu_delegate.cc', 'atom/browser/ui/views/menu_delegate.h', - 'atom/browser/ui/views/menu_layout.cc', - 'atom/browser/ui/views/menu_layout.h', 'atom/browser/ui/views/menu_model_adapter.cc', 'atom/browser/ui/views/menu_model_adapter.h', 'atom/browser/ui/views/native_frame_view.cc', From 06fcf2c19df127078320656dcb2a0a4500f61b8a Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 12 Apr 2017 14:40:31 +0300 Subject: [PATCH 3/5] Add support for BrowserView autoresizing --- atom/browser/api/atom_api_browser_view.cc | 34 +++++++++++++++++++++++ atom/browser/api/atom_api_browser_view.h | 2 ++ atom/browser/native_browser_view.h | 6 ++++ atom/browser/native_browser_view_mac.h | 1 + atom/browser/native_browser_view_mac.mm | 22 ++++++++++++++- atom/browser/native_browser_view_views.h | 6 ++++ atom/browser/native_window_views.cc | 31 +++++++++++++++++++-- docs/api/browser-view.md | 8 ++++++ spec/api-browser-view-spec.js | 15 ++++++++++ 9 files changed, 121 insertions(+), 4 deletions(-) diff --git a/atom/browser/api/atom_api_browser_view.cc b/atom/browser/api/atom_api_browser_view.cc index 67533e645d..dc17fce9e9 100644 --- a/atom/browser/api/atom_api_browser_view.cc +++ b/atom/browser/api/atom_api_browser_view.cc @@ -16,6 +16,35 @@ #include "native_mate/dictionary.h" #include "ui/gfx/geometry/rect.h" +namespace mate { + +template <> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + atom::AutoResizeFlags* auto_resize_flags) { + mate::Dictionary params; + if (!ConvertFromV8(isolate, val, ¶ms)) { + return false; + } + + uint8_t flags = 0; + bool width = false; + if (params.Get("width", &width) && width) { + flags |= atom::kAutoResizeWidth; + } + bool height = false; + if (params.Get("height", &height) && height) { + flags |= atom::kAutoResizeHeight; + } + + *auto_resize_flags = static_cast(flags); + return true; + } +}; + +} // namespace mate + namespace atom { namespace api { @@ -73,6 +102,10 @@ int32_t BrowserView::ID() const { return weak_map_id(); } +void BrowserView::SetAutoResize(AutoResizeFlags flags) { + view_->SetAutoResizeFlags(flags); +} + void BrowserView::SetBounds(const gfx::Rect& bounds) { view_->SetBounds(bounds); } @@ -95,6 +128,7 @@ void BrowserView::BuildPrototype(v8::Isolate* isolate, prototype->SetClassName(mate::StringToV8(isolate, "BrowserView")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) .MakeDestroyable() + .SetMethod("setAutoResize", &BrowserView::SetAutoResize) .SetMethod("setBounds", &BrowserView::SetBounds) .SetMethod("setBackgroundColor", &BrowserView::SetBackgroundColor) .SetProperty("webContents", &BrowserView::WebContents) diff --git a/atom/browser/api/atom_api_browser_view.h b/atom/browser/api/atom_api_browser_view.h index 875d842898..7531cfcc4a 100644 --- a/atom/browser/api/atom_api_browser_view.h +++ b/atom/browser/api/atom_api_browser_view.h @@ -9,6 +9,7 @@ #include #include "atom/browser/api/trackable_object.h" +#include "atom/browser/native_browser_view.h" #include "native_mate/handle.h" namespace gfx { @@ -50,6 +51,7 @@ class BrowserView : public mate::TrackableObject { v8::Local wrapper, const mate::Dictionary& options); + void SetAutoResize(AutoResizeFlags flags); void SetBounds(const gfx::Rect& bounds); void SetBackgroundColor(const std::string& color_name); diff --git a/atom/browser/native_browser_view.h b/atom/browser/native_browser_view.h index f9af80f65e..4216cc1e34 100644 --- a/atom/browser/native_browser_view.h +++ b/atom/browser/native_browser_view.h @@ -22,6 +22,11 @@ namespace api { class WebContents; } +enum AutoResizeFlags { + kAutoResizeWidth = 0x1, + kAutoResizeHeight = 0x2, +}; + class NativeBrowserView { public: virtual ~NativeBrowserView(); @@ -33,6 +38,7 @@ class NativeBrowserView { return web_contents_view_; } + virtual void SetAutoResizeFlags(uint8_t flags) = 0; virtual void SetBounds(const gfx::Rect& bounds) = 0; virtual void SetBackgroundColor(SkColor color) = 0; diff --git a/atom/browser/native_browser_view_mac.h b/atom/browser/native_browser_view_mac.h index 3053a098fc..4e7aa429ce 100644 --- a/atom/browser/native_browser_view_mac.h +++ b/atom/browser/native_browser_view_mac.h @@ -17,6 +17,7 @@ class NativeBrowserViewMac : public NativeBrowserView { brightray::InspectableWebContentsView* web_contents_view); ~NativeBrowserViewMac() override; + void SetAutoResizeFlags(uint8_t flags) override; void SetBounds(const gfx::Rect& bounds) override; void SetBackgroundColor(SkColor color) override; diff --git a/atom/browser/native_browser_view_mac.mm b/atom/browser/native_browser_view_mac.mm index 73a36cd347..2ce2adc1f4 100644 --- a/atom/browser/native_browser_view_mac.mm +++ b/atom/browser/native_browser_view_mac.mm @@ -8,14 +8,34 @@ #include "skia/ext/skia_utils_mac.h" #include "ui/gfx/geometry/rect.h" +// Match view::Views behavior where the view sticks to the top-left origin. +const NSAutoresizingMaskOptions kDefaultAutoResizingMask = + NSViewMaxXMargin | NSViewMinYMargin; + namespace atom { NativeBrowserViewMac::NativeBrowserViewMac( brightray::InspectableWebContentsView* web_contents_view) - : NativeBrowserView(web_contents_view) {} + : NativeBrowserView(web_contents_view) { + auto* view = GetInspectableWebContentsView()->GetNativeView(); + view.autoresizingMask = kDefaultAutoResizingMask; +} NativeBrowserViewMac::~NativeBrowserViewMac() {} +void NativeBrowserViewMac::SetAutoResizeFlags(uint8_t flags) { + NSAutoresizingMaskOptions autoresizing_mask = kDefaultAutoResizingMask; + if (flags & kAutoResizeWidth) { + autoresizing_mask |= NSViewWidthSizable; + } + if (flags & kAutoResizeHeight) { + autoresizing_mask |= NSViewHeightSizable; + } + + auto* view = GetInspectableWebContentsView()->GetNativeView(); + view.autoresizingMask = autoresizing_mask; +} + void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) { auto* view = GetInspectableWebContentsView()->GetNativeView(); auto* superview = view.superview; diff --git a/atom/browser/native_browser_view_views.h b/atom/browser/native_browser_view_views.h index ecfc6989df..5dcda13447 100644 --- a/atom/browser/native_browser_view_views.h +++ b/atom/browser/native_browser_view_views.h @@ -15,10 +15,16 @@ class NativeBrowserViewViews : public NativeBrowserView { brightray::InspectableWebContentsView* web_contents_view); ~NativeBrowserViewViews() override; + uint8_t GetAutoResizeFlags() { return auto_resize_flags_; } + void SetAutoResizeFlags(uint8_t flags) override { + auto_resize_flags_ = flags; + } void SetBounds(const gfx::Rect& bounds) override; void SetBackgroundColor(SkColor color) override; private: + uint8_t auto_resize_flags_; + DISALLOW_COPY_AND_ASSIGN(NativeBrowserViewViews); }; diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 8f5072931b..615c848daa 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -7,7 +7,7 @@ #include #include -#include "atom/browser/native_browser_view.h" +#include "atom/browser/native_browser_view_views.h" #include "atom/browser/ui/views/menu_bar.h" #include "atom/browser/window_list.h" #include "atom/common/color_util.h" @@ -895,7 +895,8 @@ void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) { void NativeWindowViews::SetBrowserView(NativeBrowserView* browser_view) { if (browser_view_) { - RemoveChildView(browser_view_->GetInspectableWebContentsView()->GetView()); + web_view_->RemoveChildView( + browser_view_->GetInspectableWebContentsView()->GetView()); browser_view_ = nullptr; } @@ -903,8 +904,11 @@ void NativeWindowViews::SetBrowserView(NativeBrowserView* browser_view) { return; } + // Add as child of the main web view to avoid (0, 0) origin from overlapping + // with menu bar. browser_view_ = browser_view; - AddChildView(browser_view->GetInspectableWebContentsView()->GetView()); + web_view_->AddChildView( + browser_view->GetInspectableWebContentsView()->GetView()); } void NativeWindowViews::SetParentWindow(NativeWindow* parent) { @@ -1292,11 +1296,32 @@ void NativeWindowViews::Layout() { menu_bar_->SetBoundsRect(menu_bar_bounds); } + const auto old_web_view_size = web_view_ ? web_view_->size() : gfx::Size(); if (web_view_) { web_view_->SetBoundsRect( gfx::Rect(0, menu_bar_bounds.height(), size.width(), size.height() - menu_bar_bounds.height())); } + const auto new_web_view_size = web_view_ ? web_view_->size() : gfx::Size(); + + if (browser_view_) { + const auto flags = static_cast(browser_view_) + ->GetAutoResizeFlags(); + int width_delta = 0; + int height_delta = 0; + if (flags & kAutoResizeWidth) { + width_delta = new_web_view_size.width() - old_web_view_size.width(); + } + if (flags & kAutoResizeHeight) { + height_delta = new_web_view_size.height() - old_web_view_size.height(); + } + + auto* view = browser_view_->GetInspectableWebContentsView()->GetView(); + auto new_view_size = view->size(); + new_view_size.set_width(new_view_size.width() + width_delta); + new_view_size.set_height(new_view_size.height() + height_delta); + view->SetSize(new_view_size); + } } gfx::Size NativeWindowViews::GetMinimumSize() { diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index 1fa518fdd7..c6d8fb5904 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -50,6 +50,14 @@ A `Integer` representing the unique ID of the view. Objects created with `new BrowserWindow` have the following instance methods: +#### `win.setAutoResize(options)` _Experimental_ + +* `options` Object + * `width`: If `true`, the view's width will grow and shrink together with + the window. `false` by default. + * `height`: If `true`, the view's height will grow and shrink together with + the window. `false` by default. + #### `win.setBounds(bounds)` _Experimental_ * `bounds` [Rectangle](structures/rectangle.md) diff --git a/spec/api-browser-view-spec.js b/spec/api-browser-view-spec.js index d4ab02a417..fc138b0b6f 100644 --- a/spec/api-browser-view-spec.js +++ b/spec/api-browser-view-spec.js @@ -38,6 +38,21 @@ describe('View module', function () { }) }) + describe('BrowserView.setAutoResize()', function () { + it('does not throw for valid args', function () { + const view = new BrowserView() + view.setAutoResize({}) + view.setAutoResize({ width: true, height: false }) + }) + + it('throws for invalid args', function () { + const view = new BrowserView() + assert.throws(function () { + view.setAutoResize(null) + }, /conversion failure/) + }) + }) + describe('BrowserView.setBounds()', function () { it('does not throw for valid args', function () { const view = new BrowserView() From ccdeb4746ec25555b11709b19742e1f1b7fe7cb8 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Thu, 13 Apr 2017 00:52:07 +0300 Subject: [PATCH 4/5] Destroy BrowserViews after each test --- spec/api-browser-view-spec.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/spec/api-browser-view-spec.js b/spec/api-browser-view-spec.js index fc138b0b6f..3ccb9502c2 100644 --- a/spec/api-browser-view-spec.js +++ b/spec/api-browser-view-spec.js @@ -8,6 +8,7 @@ const {BrowserView, BrowserWindow} = remote describe('View module', function () { var w = null + var view = null beforeEach(function () { w = new BrowserWindow({ @@ -21,17 +22,22 @@ describe('View module', function () { }) afterEach(function () { + if (view) { + view.destroy() + view = null + } + return closeWindow(w).then(function () { w = null }) }) describe('BrowserView.setBackgroundColor()', function () { it('does not throw for valid args', function () { - const view = new BrowserView() + view = new BrowserView() view.setBackgroundColor('#000') }) it('throws for invalid args', function () { - const view = new BrowserView() + view = new BrowserView() assert.throws(function () { view.setBackgroundColor(null) }, /conversion failure/) @@ -40,13 +46,13 @@ describe('View module', function () { describe('BrowserView.setAutoResize()', function () { it('does not throw for valid args', function () { - const view = new BrowserView() + view = new BrowserView() view.setAutoResize({}) view.setAutoResize({ width: true, height: false }) }) it('throws for invalid args', function () { - const view = new BrowserView() + view = new BrowserView() assert.throws(function () { view.setAutoResize(null) }, /conversion failure/) @@ -55,12 +61,12 @@ describe('View module', function () { describe('BrowserView.setBounds()', function () { it('does not throw for valid args', function () { - const view = new BrowserView() + view = new BrowserView() view.setBounds({ x: 0, y: 0, width: 1, height: 1 }) }) it('throws for invalid args', function () { - const view = new BrowserView() + view = new BrowserView() assert.throws(function () { view.setBounds(null) }, /conversion failure/) @@ -72,12 +78,12 @@ describe('View module', function () { describe('BrowserWindow.setBrowserView()', function () { it('does not throw for valid args', function () { - const view = new BrowserView() + view = new BrowserView() w.setBrowserView(view) }) it('does not throw if called multiple times with same view', function () { - const view = new BrowserView() + view = new BrowserView() w.setBrowserView(view) w.setBrowserView(view) w.setBrowserView(view) From 000aedf2e796788f5375b5a26752e7c645b1f2b9 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Thu, 13 Apr 2017 01:05:19 +0300 Subject: [PATCH 5/5] Avoid insecure nodeIntegration in example --- docs/api/browser-view.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api/browser-view.md b/docs/api/browser-view.md index c6d8fb5904..3b7e6f9b9b 100644 --- a/docs/api/browser-view.md +++ b/docs/api/browser-view.md @@ -23,7 +23,11 @@ win.on('closed', () => { win = null }) -let view = new BrowserView() +let view = new BrowserView({ + webPreferences: { + nodeIntegration: false + } +}) win.addChildView(view) view.setBounds(0, 0, 300, 300) view.webContents.loadURL('https://electron.atom.io')