feat: replace BrowserView with WebContentsView (#35658)
This commit is contained in:
parent
a94fb2cb5d
commit
15c6014324
76 changed files with 2987 additions and 1531 deletions
7
BUILD.gn
7
BUILD.gn
|
@ -692,13 +692,6 @@ source_set("electron_lib") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable_views_api) {
|
|
||||||
sources += [
|
|
||||||
"shell/browser/api/views/electron_api_image_view.cc",
|
|
||||||
"shell/browser/api/views/electron_api_image_view.h",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable_printing) {
|
if (enable_printing) {
|
||||||
sources += [
|
sources += [
|
||||||
"shell/browser/printing/print_view_manager_electron.cc",
|
"shell/browser/printing/print_view_manager_electron.cc",
|
||||||
|
|
|
@ -279,8 +279,8 @@ for:
|
||||||
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/$artifact_name" -OutFile $outfile
|
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/$artifact_name" -OutFile $outfile
|
||||||
}
|
}
|
||||||
# Uncomment the following lines to download the pdb.zip to show real stacktraces when crashes happen during testing
|
# Uncomment the following lines to download the pdb.zip to show real stacktraces when crashes happen during testing
|
||||||
# Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/pdb.zip" -OutFile pdb.zip
|
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/pdb.zip" -OutFile pdb.zip
|
||||||
# 7z x -y -osrc pdb.zip
|
7z x -y -osrc pdb.zip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
- ps: |
|
- ps: |
|
||||||
|
|
|
@ -53,12 +53,6 @@ module.exports = ({
|
||||||
|
|
||||||
const ignoredModules = [];
|
const ignoredModules = [];
|
||||||
|
|
||||||
if (defines.ENABLE_VIEWS_API === 'false') {
|
|
||||||
ignoredModules.push(
|
|
||||||
'@electron/internal/browser/api/views/image-view.js'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const plugins = [];
|
const plugins = [];
|
||||||
|
|
||||||
if (onlyPrintingGraph) {
|
if (onlyPrintingGraph) {
|
||||||
|
|
|
@ -9,7 +9,6 @@ buildflag_header("buildflags") {
|
||||||
header = "buildflags.h"
|
header = "buildflags.h"
|
||||||
|
|
||||||
flags = [
|
flags = [
|
||||||
"ENABLE_VIEWS_API=$enable_views_api",
|
|
||||||
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
||||||
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
|
"ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions",
|
||||||
"ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker",
|
"ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker",
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
declare_args() {
|
declare_args() {
|
||||||
enable_views_api = true
|
|
||||||
|
|
||||||
enable_pdf_viewer = true
|
enable_pdf_viewer = true
|
||||||
|
|
||||||
# Provide a fake location provider for mocking
|
# Provide a fake location provider for mocking
|
||||||
|
|
|
@ -106,7 +106,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||||
|
|
||||||
* [app](api/app.md)
|
* [app](api/app.md)
|
||||||
* [autoUpdater](api/auto-updater.md)
|
* [autoUpdater](api/auto-updater.md)
|
||||||
* [BrowserView](api/browser-view.md)
|
* [BaseWindow](api/base-window.md)
|
||||||
* [BrowserWindow](api/browser-window.md)
|
* [BrowserWindow](api/browser-window.md)
|
||||||
* [contentTracing](api/content-tracing.md)
|
* [contentTracing](api/content-tracing.md)
|
||||||
* [desktopCapturer](api/desktop-capturer.md)
|
* [desktopCapturer](api/desktop-capturer.md)
|
||||||
|
@ -134,8 +134,10 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||||
* [TouchBar](api/touch-bar.md)
|
* [TouchBar](api/touch-bar.md)
|
||||||
* [Tray](api/tray.md)
|
* [Tray](api/tray.md)
|
||||||
* [utilityProcess](api/utility-process.md)
|
* [utilityProcess](api/utility-process.md)
|
||||||
|
* [View](api/view.md)
|
||||||
* [webContents](api/web-contents.md)
|
* [webContents](api/web-contents.md)
|
||||||
* [webFrameMain](api/web-frame-main.md)
|
* [webFrameMain](api/web-frame-main.md)
|
||||||
|
* [WebContentsView](api/web-contents-view.md)
|
||||||
|
|
||||||
### Modules for the Renderer Process (Web Page):
|
### Modules for the Renderer Process (Web Page):
|
||||||
|
|
||||||
|
|
1380
docs/api/base-window.md
Normal file
1380
docs/api/base-window.md
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,9 @@
|
||||||
# BrowserView
|
# BrowserView
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
A `BrowserView` can be used to embed additional web content into a
|
A `BrowserView` can be used to embed additional web content into a
|
||||||
[`BrowserWindow`](browser-window.md). It is like a child window, except that it is positioned
|
[`BrowserWindow`](browser-window.md). 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
|
relative to its owning window. It is meant to be an alternative to the
|
||||||
|
@ -9,6 +13,10 @@ relative to its owning window. It is meant to be an alternative to the
|
||||||
|
|
||||||
> Create and control views.
|
> Create and control views.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
Process: [Main](../glossary.md#main-process)
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
This module cannot be used until the `ready` event of the `app`
|
This module cannot be used until the `ready` event of the `app`
|
||||||
|
@ -30,7 +38,7 @@ app.whenReady().then(() => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### `new BrowserView([options])` _Experimental_
|
### `new BrowserView([options])` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `options` Object (optional)
|
* `options` Object (optional)
|
||||||
* `webPreferences` [WebPreferences](structures/web-preferences.md?inline) (optional) - Settings of web page's features.
|
* `webPreferences` [WebPreferences](structures/web-preferences.md?inline) (optional) - Settings of web page's features.
|
||||||
|
@ -39,7 +47,7 @@ app.whenReady().then(() => {
|
||||||
|
|
||||||
Objects created with `new BrowserView` have the following properties:
|
Objects created with `new BrowserView` have the following properties:
|
||||||
|
|
||||||
#### `view.webContents` _Experimental_
|
#### `view.webContents` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
A [`WebContents`](web-contents.md) object owned by this view.
|
A [`WebContents`](web-contents.md) object owned by this view.
|
||||||
|
|
||||||
|
@ -47,7 +55,7 @@ A [`WebContents`](web-contents.md) object owned by this view.
|
||||||
|
|
||||||
Objects created with `new BrowserView` have the following instance methods:
|
Objects created with `new BrowserView` have the following instance methods:
|
||||||
|
|
||||||
#### `view.setAutoResize(options)` _Experimental_
|
#### `view.setAutoResize(options)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `options` Object
|
* `options` Object
|
||||||
* `width` boolean (optional) - If `true`, the view's width will grow and shrink together
|
* `width` boolean (optional) - If `true`, the view's width will grow and shrink together
|
||||||
|
@ -59,19 +67,19 @@ Objects created with `new BrowserView` have the following instance methods:
|
||||||
* `vertical` boolean (optional) - If `true`, the view's y position and height will grow
|
* `vertical` boolean (optional) - If `true`, the view's y position and height will grow
|
||||||
and shrink proportionally with the window. `false` by default.
|
and shrink proportionally with the window. `false` by default.
|
||||||
|
|
||||||
#### `view.setBounds(bounds)` _Experimental_
|
#### `view.setBounds(bounds)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `bounds` [Rectangle](structures/rectangle.md)
|
* `bounds` [Rectangle](structures/rectangle.md)
|
||||||
|
|
||||||
Resizes and moves the view to the supplied bounds relative to the window.
|
Resizes and moves the view to the supplied bounds relative to the window.
|
||||||
|
|
||||||
#### `view.getBounds()` _Experimental_
|
#### `view.getBounds()` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
Returns [`Rectangle`](structures/rectangle.md)
|
Returns [`Rectangle`](structures/rectangle.md)
|
||||||
|
|
||||||
The `bounds` of this BrowserView instance as `Object`.
|
The `bounds` of this BrowserView instance as `Object`.
|
||||||
|
|
||||||
#### `view.setBackgroundColor(color)` _Experimental_
|
#### `view.setBackgroundColor(color)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `color` string - Color in Hex, RGB, ARGB, HSL, HSLA or named CSS color format. The alpha channel is
|
* `color` string - Color in Hex, RGB, ARGB, HSL, HSLA or named CSS color format. The alpha channel is
|
||||||
optional for the hex type.
|
optional for the hex type.
|
||||||
|
@ -79,25 +87,25 @@ The `bounds` of this BrowserView instance as `Object`.
|
||||||
Examples of valid `color` values:
|
Examples of valid `color` values:
|
||||||
|
|
||||||
* Hex
|
* Hex
|
||||||
* #fff (RGB)
|
* `#fff` (RGB)
|
||||||
* #ffff (ARGB)
|
* `#ffff` (ARGB)
|
||||||
* #ffffff (RRGGBB)
|
* `#ffffff` (RRGGBB)
|
||||||
* #ffffffff (AARRGGBB)
|
* `#ffffffff` (AARRGGBB)
|
||||||
* RGB
|
* RGB
|
||||||
* rgb\((\[\d]+),\s*(\[\d]+),\s*(\[\d]+)\)
|
* `rgb\(([\d]+),\s*([\d]+),\s*([\d]+)\)`
|
||||||
* e.g. rgb(255, 255, 255)
|
* e.g. `rgb(255, 255, 255)`
|
||||||
* RGBA
|
* RGBA
|
||||||
* rgba\((\[\d]+),\s*(\[\d]+),\s*(\[\d]+),\s*(\[\d.]+)\)
|
* `rgba\(([\d]+),\s*([\d]+),\s*([\d]+),\s*([\d.]+)\)`
|
||||||
* e.g. rgba(255, 255, 255, 1.0)
|
* e.g. `rgba(255, 255, 255, 1.0)`
|
||||||
* HSL
|
* HSL
|
||||||
* hsl\((-?\[\d.]+),\s*(\[\d.]+)%,\s*(\[\d.]+)%\)
|
* `hsl\((-?[\d.]+),\s*([\d.]+)%,\s*([\d.]+)%\)`
|
||||||
* e.g. hsl(200, 20%, 50%)
|
* e.g. `hsl(200, 20%, 50%)`
|
||||||
* HSLA
|
* HSLA
|
||||||
* hsla\((-?\[\d.]+),\s*(\[\d.]+)%,\s*(\[\d.]+)%,\s*(\[\d.]+)\)
|
* `hsla\((-?[\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)`
|
||||||
* e.g. hsla(200, 20%, 50%, 0.5)
|
* e.g. `hsla(200, 20%, 50%, 0.5)`
|
||||||
* Color name
|
* Color name
|
||||||
* Options are listed in [SkParseColor.cpp](https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/utils/SkParseColor.cpp;l=11-152;drc=eea4bf52cb0d55e2a39c828b017c80a5ee054148)
|
* Options are listed in [SkParseColor.cpp](https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/utils/SkParseColor.cpp;l=11-152;drc=eea4bf52cb0d55e2a39c828b017c80a5ee054148)
|
||||||
* Similar to CSS Color Module Level 3 keywords, but case-sensitive.
|
* Similar to CSS Color Module Level 3 keywords, but case-sensitive.
|
||||||
* e.g. `blueviolet` or `red`
|
* e.g. `blueviolet` or `red`
|
||||||
|
|
||||||
**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBA` or `RGA`.
|
**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`.
|
||||||
|
|
|
@ -98,8 +98,8 @@ The `child` window will always show on top of the `top` window.
|
||||||
|
|
||||||
## Modal windows
|
## Modal windows
|
||||||
|
|
||||||
A modal window is a child window that disables parent window, to create a modal
|
A modal window is a child window that disables parent window. To create a modal
|
||||||
window, you have to set both `parent` and `modal` options:
|
window, you have to set both the `parent` and `modal` options:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { BrowserWindow } = require('electron')
|
const { BrowserWindow } = require('electron')
|
||||||
|
@ -140,7 +140,7 @@ state is `hidden` in order to minimize power consumption.
|
||||||
* On Linux the type of modal windows will be changed to `dialog`.
|
* On Linux the type of modal windows will be changed to `dialog`.
|
||||||
* On Linux many desktop environments do not support hiding a modal window.
|
* On Linux many desktop environments do not support hiding a modal window.
|
||||||
|
|
||||||
## Class: BrowserWindow
|
## Class: BrowserWindow extends `BaseWindow`
|
||||||
|
|
||||||
> Create and control browser windows.
|
> Create and control browser windows.
|
||||||
|
|
||||||
|
@ -440,10 +440,14 @@ Returns `BrowserWindow | null` - The window that is focused in this application,
|
||||||
Returns `BrowserWindow | null` - The window that owns the given `webContents`
|
Returns `BrowserWindow | null` - The window that owns the given `webContents`
|
||||||
or `null` if the contents are not owned by a window.
|
or `null` if the contents are not owned by a window.
|
||||||
|
|
||||||
#### `BrowserWindow.fromBrowserView(browserView)`
|
#### `BrowserWindow.fromBrowserView(browserView)` _Deprecated_
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md)
|
* `browserView` [BrowserView](browser-view.md)
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
Returns `BrowserWindow | null` - The window that owns the given `browserView`. If the given view is not attached to any window, returns `null`.
|
Returns `BrowserWindow | null` - The window that owns the given `browserView`. If the given view is not attached to any window, returns `null`.
|
||||||
|
|
||||||
#### `BrowserWindow.fromId(id)`
|
#### `BrowserWindow.fromId(id)`
|
||||||
|
@ -1580,41 +1584,62 @@ machine has a touch bar.
|
||||||
**Note:** The TouchBar API is currently experimental and may change or be
|
**Note:** The TouchBar API is currently experimental and may change or be
|
||||||
removed in future Electron releases.
|
removed in future Electron releases.
|
||||||
|
|
||||||
#### `win.setBrowserView(browserView)` _Experimental_
|
#### `win.setBrowserView(browserView)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md) | null - Attach `browserView` to `win`.
|
* `browserView` [BrowserView](browser-view.md) | null - Attach `browserView` to `win`.
|
||||||
If there are other `BrowserView`s attached, they will be removed from
|
If there are other `BrowserView`s attached, they will be removed from
|
||||||
this window.
|
this window.
|
||||||
|
|
||||||
#### `win.getBrowserView()` _Experimental_
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
|
#### `win.getBrowserView()` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
Returns `BrowserView | null` - The `BrowserView` attached to `win`. Returns `null`
|
Returns `BrowserView | null` - The `BrowserView` attached to `win`. Returns `null`
|
||||||
if one is not attached. Throws an error if multiple `BrowserView`s are attached.
|
if one is not attached. Throws an error if multiple `BrowserView`s are attached.
|
||||||
|
|
||||||
#### `win.addBrowserView(browserView)` _Experimental_
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
|
#### `win.addBrowserView(browserView)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md)
|
* `browserView` [BrowserView](browser-view.md)
|
||||||
|
|
||||||
Replacement API for setBrowserView supporting work with multi browser views.
|
Replacement API for setBrowserView supporting work with multi browser views.
|
||||||
|
|
||||||
#### `win.removeBrowserView(browserView)` _Experimental_
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
|
#### `win.removeBrowserView(browserView)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md)
|
* `browserView` [BrowserView](browser-view.md)
|
||||||
|
|
||||||
#### `win.setTopBrowserView(browserView)` _Experimental_
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
|
#### `win.setTopBrowserView(browserView)` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
* `browserView` [BrowserView](browser-view.md)
|
* `browserView` [BrowserView](browser-view.md)
|
||||||
|
|
||||||
Raises `browserView` above other `BrowserView`s attached to `win`.
|
Raises `browserView` above other `BrowserView`s attached to `win`.
|
||||||
Throws an error if `browserView` is not attached to `win`.
|
Throws an error if `browserView` is not attached to `win`.
|
||||||
|
|
||||||
#### `win.getBrowserViews()` _Experimental_
|
> **Note**
|
||||||
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
|
#### `win.getBrowserViews()` _Experimental_ _Deprecated_
|
||||||
|
|
||||||
Returns `BrowserView[]` - a sorted by z-index array of all BrowserViews that have been attached
|
Returns `BrowserView[]` - a sorted by z-index array of all BrowserViews that have been attached
|
||||||
with `addBrowserView` or `setBrowserView`. The top-most BrowserView is the last element of the array.
|
with `addBrowserView` or `setBrowserView`. The top-most BrowserView is the last element of the array.
|
||||||
|
|
||||||
**Note:** The BrowserView API is currently experimental and may change or be
|
> **Note**
|
||||||
removed in future Electron releases.
|
> The `BrowserView` class is deprecated, and replaced by the new
|
||||||
|
> [`WebContentsView`](web-contents-view.md) class.
|
||||||
|
|
||||||
#### `win.setTitleBarOverlay(options)` _Windows_
|
#### `win.setTitleBarOverlay(options)` _Windows_
|
||||||
|
|
||||||
|
|
|
@ -142,11 +142,6 @@ Setting this variable is the same as passing `--log-file`
|
||||||
on the command line. For more info, see `--log-file` in [command-line
|
on the command line. For more info, see `--log-file` in [command-line
|
||||||
switches](./command-line-switches.md#--log-filepath).
|
switches](./command-line-switches.md#--log-filepath).
|
||||||
|
|
||||||
### `ELECTRON_DEBUG_DRAG_REGIONS`
|
|
||||||
|
|
||||||
Adds coloration to draggable regions on [`BrowserView`](./browser-view.md)s on macOS - draggable regions will be colored
|
|
||||||
green and non-draggable regions will be colored red to aid debugging.
|
|
||||||
|
|
||||||
### `ELECTRON_DEBUG_NOTIFICATIONS`
|
### `ELECTRON_DEBUG_NOTIFICATIONS`
|
||||||
|
|
||||||
Adds extra logs to [`Notification`](./notification.md) lifecycles on macOS to aid in debugging. Extra logging will be displayed when new Notifications are created or activated. They will also be displayed when common actions are taken: a notification is shown, dismissed, its button is clicked, or it is replied to.
|
Adds extra logs to [`Notification`](./notification.md) lifecycles on macOS to aid in debugging. Extra logging will be displayed when new Notifications are created or activated. They will also be displayed when common actions are taken: a notification is shown, dismissed, its button is clicked, or it is replied to.
|
||||||
|
|
152
docs/api/structures/base-window-options.md
Normal file
152
docs/api/structures/base-window-options.md
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
# BaseWindowConstructorOptions Object
|
||||||
|
|
||||||
|
* `width` Integer (optional) - Window's width in pixels. Default is `800`.
|
||||||
|
* `height` Integer (optional) - Window's height in pixels. Default is `600`.
|
||||||
|
* `x` Integer (optional) - (**required** if y is used) Window's left offset from screen.
|
||||||
|
Default is to center the window.
|
||||||
|
* `y` Integer (optional) - (**required** if x is used) Window's top offset from screen.
|
||||||
|
Default is to center the window.
|
||||||
|
* `useContentSize` boolean (optional) - The `width` and `height` would be used as web
|
||||||
|
page's size, which means the actual window's size will include window
|
||||||
|
frame's size and be slightly larger. Default is `false`.
|
||||||
|
* `center` boolean (optional) - Show window in the center of the screen. Default is `false`.
|
||||||
|
* `minWidth` Integer (optional) - Window's minimum width. Default is `0`.
|
||||||
|
* `minHeight` Integer (optional) - Window's minimum height. Default is `0`.
|
||||||
|
* `maxWidth` Integer (optional) - Window's maximum width. Default is no limit.
|
||||||
|
* `maxHeight` Integer (optional) - Window's maximum height. Default is no limit.
|
||||||
|
* `resizable` boolean (optional) - Whether window is resizable. Default is `true`.
|
||||||
|
* `movable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
||||||
|
movable. This is not implemented on Linux. Default is `true`.
|
||||||
|
* `minimizable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
||||||
|
minimizable. This is not implemented on Linux. Default is `true`.
|
||||||
|
* `maximizable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
||||||
|
maximizable. This is not implemented on Linux. Default is `true`.
|
||||||
|
* `closable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
||||||
|
closable. This is not implemented on Linux. Default is `true`.
|
||||||
|
* `focusable` boolean (optional) - Whether the window can be focused. Default is
|
||||||
|
`true`. On Windows setting `focusable: false` also implies setting
|
||||||
|
`skipTaskbar: true`. On Linux setting `focusable: false` makes the window
|
||||||
|
stop interacting with wm, so the window will always stay on top in all
|
||||||
|
workspaces.
|
||||||
|
* `alwaysOnTop` boolean (optional) - Whether the window should always stay on top of
|
||||||
|
other windows. Default is `false`.
|
||||||
|
* `fullscreen` boolean (optional) - Whether the window should show in fullscreen. When
|
||||||
|
explicitly set to `false` the fullscreen button will be hidden or disabled
|
||||||
|
on macOS. Default is `false`.
|
||||||
|
* `fullscreenable` boolean (optional) - Whether the window can be put into fullscreen
|
||||||
|
mode. On macOS, also whether the maximize/zoom button should toggle full
|
||||||
|
screen mode or maximize window. Default is `true`.
|
||||||
|
* `simpleFullscreen` boolean (optional) _macOS_ - Use pre-Lion fullscreen on
|
||||||
|
macOS. Default is `false`.
|
||||||
|
* `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar.
|
||||||
|
Default is `false`.
|
||||||
|
* `hiddenInMissionControl` boolean (optional) _macOS_ - Whether window should be hidden when the user toggles into mission control.
|
||||||
|
* `kiosk` boolean (optional) - Whether the window is in kiosk mode. Default is `false`.
|
||||||
|
* `title` string (optional) - Default window title. Default is `"Electron"`. If the HTML tag `<title>` is defined in the HTML file loaded by `loadURL()`, this property will be ignored.
|
||||||
|
* `icon` ([NativeImage](../native-image.md) | string) (optional) - The window icon. On Windows it is
|
||||||
|
recommended to use `ICO` icons to get best visual effects, you can also
|
||||||
|
leave it undefined so the executable's icon will be used.
|
||||||
|
* `show` boolean (optional) - Whether window should be shown when created. Default is
|
||||||
|
`true`.
|
||||||
|
* `frame` boolean (optional) - Specify `false` to create a
|
||||||
|
[frameless window](../../tutorial/window-customization.md#create-frameless-windows). Default is `true`.
|
||||||
|
* `parent` BaseWindow (optional) - Specify parent window. Default is `null`.
|
||||||
|
* `modal` boolean (optional) - Whether this is a modal window. This only works when the
|
||||||
|
window is a child window. Default is `false`.
|
||||||
|
* `acceptFirstMouse` boolean (optional) _macOS_ - Whether clicking an
|
||||||
|
inactive window will also click through to the web contents. Default is
|
||||||
|
`false` on macOS. This option is not configurable on other platforms.
|
||||||
|
* `disableAutoHideCursor` boolean (optional) - Whether to hide cursor when typing.
|
||||||
|
Default is `false`.
|
||||||
|
* `autoHideMenuBar` boolean (optional) - Auto hide the menu bar unless the `Alt`
|
||||||
|
key is pressed. Default is `false`.
|
||||||
|
* `enableLargerThanScreen` boolean (optional) _macOS_ - Enable the window to
|
||||||
|
be resized larger than screen. Only relevant for macOS, as other OSes
|
||||||
|
allow larger-than-screen windows by default. Default is `false`.
|
||||||
|
* `backgroundColor` string (optional) - The window's background color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha in #AARRGGBB format is supported if `transparent` is set to `true`. Default is `#FFF` (white). See [win.setBackgroundColor](../browser-window.md#winsetbackgroundcolorbackgroundcolor) for more information.
|
||||||
|
* `hasShadow` boolean (optional) - Whether window should have a shadow. Default is `true`.
|
||||||
|
* `opacity` number (optional) _macOS_ _Windows_ - Set the initial opacity of
|
||||||
|
the window, between 0.0 (fully transparent) and 1.0 (fully opaque). This
|
||||||
|
is only implemented on Windows and macOS.
|
||||||
|
* `darkTheme` boolean (optional) - Forces using dark theme for the window, only works on
|
||||||
|
some GTK+3 desktop environments. Default is `false`.
|
||||||
|
* `transparent` boolean (optional) - Makes the window [transparent](../../tutorial/window-customization.md#create-transparent-windows).
|
||||||
|
Default is `false`. On Windows, does not work unless the window is frameless.
|
||||||
|
* `type` string (optional) - The type of window, default is normal window. See more about
|
||||||
|
this below.
|
||||||
|
* `visualEffectState` string (optional) _macOS_ - Specify how the material
|
||||||
|
appearance should reflect window activity state on macOS. Must be used
|
||||||
|
with the `vibrancy` property. Possible values are:
|
||||||
|
* `followWindow` - The backdrop should automatically appear active when the window is active, and inactive when it is not. This is the default.
|
||||||
|
* `active` - The backdrop should always appear active.
|
||||||
|
* `inactive` - The backdrop should always appear inactive.
|
||||||
|
* `titleBarStyle` string (optional) _macOS_ _Windows_ - The style of window title bar.
|
||||||
|
Default is `default`. Possible values are:
|
||||||
|
* `default` - Results in the standard title bar for macOS or Windows respectively.
|
||||||
|
* `hidden` - Results in a hidden title bar and a full size content window. On macOS, the window still has the standard window controls (“traffic lights”) in the top left. On Windows, when combined with `titleBarOverlay: true` it will activate the Window Controls Overlay (see `titleBarOverlay` for more information), otherwise no window controls will be shown.
|
||||||
|
* `hiddenInset` _macOS_ - Only on macOS, results in a hidden title bar
|
||||||
|
with an alternative look where the traffic light buttons are slightly
|
||||||
|
more inset from the window edge.
|
||||||
|
* `customButtonsOnHover` _macOS_ - Only on macOS, results in a hidden
|
||||||
|
title bar and a full size content window, the traffic light buttons will
|
||||||
|
display when being hovered over in the top left of the window.
|
||||||
|
**Note:** This option is currently experimental.
|
||||||
|
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
||||||
|
Set a custom position for the traffic light buttons in frameless windows.
|
||||||
|
* `roundedCorners` boolean (optional) _macOS_ - Whether frameless window
|
||||||
|
should have rounded corners on macOS. Default is `true`. Setting this property
|
||||||
|
to `false` will prevent the window from being fullscreenable.
|
||||||
|
* `thickFrame` boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on
|
||||||
|
Windows, which adds standard window frame. Setting it to `false` will remove
|
||||||
|
window shadow and window animations. Default is `true`.
|
||||||
|
* `vibrancy` string (optional) _macOS_ - Add a type of vibrancy effect to
|
||||||
|
the window, only on macOS. Can be `appearance-based`, `titlebar`, `selection`,
|
||||||
|
`menu`, `popover`, `sidebar`, `header`, `sheet`, `window`, `hud`, `fullscreen-ui`,
|
||||||
|
`tooltip`, `content`, `under-window`, or `under-page`.
|
||||||
|
* `backgroundMaterial` string (optional) _Windows_ - Set the window's
|
||||||
|
system-drawn background material, including behind the non-client area.
|
||||||
|
Can be `auto`, `none`, `mica`, `acrylic` or `tabbed`. See [win.setBackgroundMaterial](../browser-window.md#winsetbackgroundmaterialmaterial-windows) for more information.
|
||||||
|
* `zoomToPageWidth` boolean (optional) _macOS_ - Controls the behavior on
|
||||||
|
macOS when option-clicking the green stoplight button on the toolbar or by
|
||||||
|
clicking the Window > Zoom menu item. If `true`, the window will grow to
|
||||||
|
the preferred width of the web page when zoomed, `false` will cause it to
|
||||||
|
zoom to the width of the screen. This will also affect the behavior when
|
||||||
|
calling `maximize()` directly. Default is `false`.
|
||||||
|
* `tabbingIdentifier` string (optional) _macOS_ - Tab group name, allows
|
||||||
|
opening the window as a native tab. Windows with the same
|
||||||
|
tabbing identifier will be grouped together. This also adds a native new
|
||||||
|
tab button to your window's tab bar and allows your `app` and window to
|
||||||
|
receive the `new-window-for-tab` event.
|
||||||
|
|
||||||
|
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||||
|
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||||
|
passing a size that does not follow size constraints to `setBounds`/`setSize` or
|
||||||
|
to the constructor of `BrowserWindow`.
|
||||||
|
|
||||||
|
The possible values and behaviors of the `type` option are platform dependent.
|
||||||
|
Possible values are:
|
||||||
|
|
||||||
|
* On Linux, possible types are `desktop`, `dock`, `toolbar`, `splash`,
|
||||||
|
`notification`.
|
||||||
|
* The `desktop` type places the window at the desktop background window level
|
||||||
|
(kCGDesktopWindowLevel - 1). However, note that a desktop window will not
|
||||||
|
receive focus, keyboard, or mouse events. You can still use globalShortcut to
|
||||||
|
receive input sparingly.
|
||||||
|
* The `dock` type creates a dock-like window behavior.
|
||||||
|
* The `toolbar` type creates a window with a toolbar appearance.
|
||||||
|
* The `splash` type behaves in a specific way. It is not
|
||||||
|
draggable, even if the CSS styling of the window's body contains
|
||||||
|
-webkit-app-region: drag. This type is commonly used for splash screens.
|
||||||
|
* The `notification` type creates a window that behaves like a system notification.
|
||||||
|
* On macOS, possible types are `desktop`, `textured`, `panel`.
|
||||||
|
* The `textured` type adds metal gradient appearance
|
||||||
|
(`NSWindowStyleMaskTexturedBackground`).
|
||||||
|
* The `desktop` type places the window at the desktop background window level
|
||||||
|
(`kCGDesktopWindowLevel - 1`). Note that desktop window will not receive
|
||||||
|
focus, keyboard or mouse events, but you can use `globalShortcut` to receive
|
||||||
|
input sparingly.
|
||||||
|
* The `panel` type enables the window to float on top of full-screened apps
|
||||||
|
by adding the `NSWindowStyleMaskNonactivatingPanel` style mask,normally
|
||||||
|
reserved for NSPanel, at runtime. Also, the window will appear on all
|
||||||
|
spaces (desktops).
|
||||||
|
* On Windows, possible type is `toolbar`.
|
|
@ -1,161 +1,11 @@
|
||||||
# BrowserWindowConstructorOptions Object
|
# BrowserWindowConstructorOptions Object extends `BaseWindowConstructorOptions`
|
||||||
|
|
||||||
* `width` Integer (optional) - Window's width in pixels. Default is `800`.
|
|
||||||
* `height` Integer (optional) - Window's height in pixels. Default is `600`.
|
|
||||||
* `x` Integer (optional) - (**required** if y is used) Window's left offset from screen.
|
|
||||||
Default is to center the window.
|
|
||||||
* `y` Integer (optional) - (**required** if x is used) Window's top offset from screen.
|
|
||||||
Default is to center the window.
|
|
||||||
* `useContentSize` boolean (optional) - The `width` and `height` would be used as web
|
|
||||||
page's size, which means the actual window's size will include window
|
|
||||||
frame's size and be slightly larger. Default is `false`.
|
|
||||||
* `center` boolean (optional) - Show window in the center of the screen. Default is `false`.
|
|
||||||
* `minWidth` Integer (optional) - Window's minimum width. Default is `0`.
|
|
||||||
* `minHeight` Integer (optional) - Window's minimum height. Default is `0`.
|
|
||||||
* `maxWidth` Integer (optional) - Window's maximum width. Default is no limit.
|
|
||||||
* `maxHeight` Integer (optional) - Window's maximum height. Default is no limit.
|
|
||||||
* `resizable` boolean (optional) - Whether window is resizable. Default is `true`.
|
|
||||||
* `movable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
|
||||||
movable. This is not implemented on Linux. Default is `true`.
|
|
||||||
* `minimizable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
|
||||||
minimizable. This is not implemented on Linux. Default is `true`.
|
|
||||||
* `maximizable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
|
||||||
maximizable. This is not implemented on Linux. Default is `true`.
|
|
||||||
* `closable` boolean (optional) _macOS_ _Windows_ - Whether window is
|
|
||||||
closable. This is not implemented on Linux. Default is `true`.
|
|
||||||
* `focusable` boolean (optional) - Whether the window can be focused. Default is
|
|
||||||
`true`. On Windows setting `focusable: false` also implies setting
|
|
||||||
`skipTaskbar: true`. On Linux setting `focusable: false` makes the window
|
|
||||||
stop interacting with wm, so the window will always stay on top in all
|
|
||||||
workspaces.
|
|
||||||
* `alwaysOnTop` boolean (optional) - Whether the window should always stay on top of
|
|
||||||
other windows. Default is `false`.
|
|
||||||
* `fullscreen` boolean (optional) - Whether the window should show in fullscreen. When
|
|
||||||
explicitly set to `false` the fullscreen button will be hidden or disabled
|
|
||||||
on macOS. Default is `false`.
|
|
||||||
* `fullscreenable` boolean (optional) - Whether the window can be put into fullscreen
|
|
||||||
mode. On macOS, also whether the maximize/zoom button should toggle full
|
|
||||||
screen mode or maximize window. Default is `true`.
|
|
||||||
* `simpleFullscreen` boolean (optional) _macOS_ - Use pre-Lion fullscreen on
|
|
||||||
macOS. Default is `false`.
|
|
||||||
* `skipTaskbar` boolean (optional) _macOS_ _Windows_ - Whether to show the window in taskbar.
|
|
||||||
Default is `false`.
|
|
||||||
* `hiddenInMissionControl` boolean (optional) _macOS_ - Whether window should be hidden when the user toggles into mission control.
|
|
||||||
* `kiosk` boolean (optional) - Whether the window is in kiosk mode. Default is `false`.
|
|
||||||
* `title` string (optional) - Default window title. Default is `"Electron"`. If the HTML tag `<title>` is defined in the HTML file loaded by `loadURL()`, this property will be ignored.
|
|
||||||
* `icon` ([NativeImage](../native-image.md) | string) (optional) - The window icon. On Windows it is
|
|
||||||
recommended to use `ICO` icons to get best visual effects, you can also
|
|
||||||
leave it undefined so the executable's icon will be used.
|
|
||||||
* `show` boolean (optional) - Whether window should be shown when created. Default is
|
|
||||||
`true`.
|
|
||||||
* `paintWhenInitiallyHidden` boolean (optional) - Whether the renderer should be active when `show` is `false` and it has just been created. In order for `document.visibilityState` to work correctly on first load with `show: false` you should set this to `false`. Setting this to `false` will cause the `ready-to-show` event to not fire. Default is `true`.
|
|
||||||
* `frame` boolean (optional) - Specify `false` to create a
|
|
||||||
[frameless window](../../tutorial/window-customization.md#create-frameless-windows). Default is `true`.
|
|
||||||
* `parent` BrowserWindow (optional) - Specify parent window. Default is `null`.
|
|
||||||
* `modal` boolean (optional) - Whether this is a modal window. This only works when the
|
|
||||||
window is a child window. Default is `false`.
|
|
||||||
* `acceptFirstMouse` boolean (optional) _macOS_ - Whether clicking an
|
|
||||||
inactive window will also click through to the web contents. Default is
|
|
||||||
`false` on macOS. This option is not configurable on other platforms.
|
|
||||||
* `disableAutoHideCursor` boolean (optional) - Whether to hide cursor when typing.
|
|
||||||
Default is `false`.
|
|
||||||
* `autoHideMenuBar` boolean (optional) - Auto hide the menu bar unless the `Alt`
|
|
||||||
key is pressed. Default is `false`.
|
|
||||||
* `enableLargerThanScreen` boolean (optional) _macOS_ - Enable the window to
|
|
||||||
be resized larger than screen. Only relevant for macOS, as other OSes
|
|
||||||
allow larger-than-screen windows by default. Default is `false`.
|
|
||||||
* `backgroundColor` string (optional) - The window's background color in Hex, RGB, RGBA, HSL, HSLA or named CSS color format. Alpha in #AARRGGBB format is supported if `transparent` is set to `true`. Default is `#FFF` (white). See [win.setBackgroundColor](../browser-window.md#winsetbackgroundcolorbackgroundcolor) for more information.
|
|
||||||
* `hasShadow` boolean (optional) - Whether window should have a shadow. Default is `true`.
|
|
||||||
* `opacity` number (optional) _macOS_ _Windows_ - Set the initial opacity of
|
|
||||||
the window, between 0.0 (fully transparent) and 1.0 (fully opaque). This
|
|
||||||
is only implemented on Windows and macOS.
|
|
||||||
* `darkTheme` boolean (optional) - Forces using dark theme for the window, only works on
|
|
||||||
some GTK+3 desktop environments. Default is `false`.
|
|
||||||
* `transparent` boolean (optional) - Makes the window [transparent](../../tutorial/window-customization.md#create-transparent-windows).
|
|
||||||
Default is `false`. On Windows, does not work unless the window is frameless.
|
|
||||||
* `type` string (optional) - The type of window, default is normal window. See more about
|
|
||||||
this below.
|
|
||||||
* `visualEffectState` string (optional) _macOS_ - Specify how the material
|
|
||||||
appearance should reflect window activity state on macOS. Must be used
|
|
||||||
with the `vibrancy` property. Possible values are:
|
|
||||||
* `followWindow` - The backdrop should automatically appear active when the window is active, and inactive when it is not. This is the default.
|
|
||||||
* `active` - The backdrop should always appear active.
|
|
||||||
* `inactive` - The backdrop should always appear inactive.
|
|
||||||
* `titleBarStyle` string (optional) _macOS_ _Windows_ - The style of window title bar.
|
|
||||||
Default is `default`. Possible values are:
|
|
||||||
* `default` - Results in the standard title bar for macOS or Windows respectively.
|
|
||||||
* `hidden` - Results in a hidden title bar and a full size content window. On macOS, the window still has the standard window controls (“traffic lights”) in the top left. On Windows, when combined with `titleBarOverlay: true` it will activate the Window Controls Overlay (see `titleBarOverlay` for more information), otherwise no window controls will be shown.
|
|
||||||
* `hiddenInset` _macOS_ - Only on macOS, results in a hidden title bar
|
|
||||||
with an alternative look where the traffic light buttons are slightly
|
|
||||||
more inset from the window edge.
|
|
||||||
* `customButtonsOnHover` _macOS_ - Only on macOS, results in a hidden
|
|
||||||
title bar and a full size content window, the traffic light buttons will
|
|
||||||
display when being hovered over in the top left of the window.
|
|
||||||
**Note:** This option is currently experimental.
|
|
||||||
* `trafficLightPosition` [Point](point.md) (optional) _macOS_ -
|
|
||||||
Set a custom position for the traffic light buttons in frameless windows.
|
|
||||||
* `roundedCorners` boolean (optional) _macOS_ - Whether frameless window
|
|
||||||
should have rounded corners on macOS. Default is `true`. Setting this property
|
|
||||||
to `false` will prevent the window from being fullscreenable.
|
|
||||||
* `thickFrame` boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on
|
|
||||||
Windows, which adds standard window frame. Setting it to `false` will remove
|
|
||||||
window shadow and window animations. Default is `true`.
|
|
||||||
* `vibrancy` string (optional) _macOS_ - Add a type of vibrancy effect to
|
|
||||||
the window, only on macOS. Can be `appearance-based`, `titlebar`, `selection`,
|
|
||||||
`menu`, `popover`, `sidebar`, `header`, `sheet`, `window`, `hud`, `fullscreen-ui`,
|
|
||||||
`tooltip`, `content`, `under-window`, or `under-page`.
|
|
||||||
* `backgroundMaterial` string (optional) _Windows_ - Set the window's
|
|
||||||
system-drawn background material, including behind the non-client area.
|
|
||||||
Can be `auto`, `none`, `mica`, `acrylic` or `tabbed`. See [win.setBackgroundMaterial](../browser-window.md#winsetbackgroundmaterialmaterial-windows) for more information.
|
|
||||||
* `zoomToPageWidth` boolean (optional) _macOS_ - Controls the behavior on
|
|
||||||
macOS when option-clicking the green stoplight button on the toolbar or by
|
|
||||||
clicking the Window > Zoom menu item. If `true`, the window will grow to
|
|
||||||
the preferred width of the web page when zoomed, `false` will cause it to
|
|
||||||
zoom to the width of the screen. This will also affect the behavior when
|
|
||||||
calling `maximize()` directly. Default is `false`.
|
|
||||||
* `tabbingIdentifier` string (optional) _macOS_ - Tab group name, allows
|
|
||||||
opening the window as a native tab. Windows with the same
|
|
||||||
tabbing identifier will be grouped together. This also adds a native new
|
|
||||||
tab button to your window's tab bar and allows your `app` and window to
|
|
||||||
receive the `new-window-for-tab` event.
|
|
||||||
* `webPreferences` [WebPreferences](web-preferences.md?inline) (optional) - Settings of web page's features.
|
* `webPreferences` [WebPreferences](web-preferences.md?inline) (optional) - Settings of web page's features.
|
||||||
|
* `paintWhenInitiallyHidden` boolean (optional) - Whether the renderer should be active when `show` is `false` and it has just been created. In order for `document.visibilityState` to work correctly on first load with `show: false` you should set this to `false`. Setting this to `false` will cause the `ready-to-show` event to not fire. Default is `true`.
|
||||||
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
|
* `titleBarOverlay` Object | Boolean (optional) - When using a frameless window in conjunction with `win.setWindowButtonVisibility(true)` on macOS or using a `titleBarStyle` so that the standard window controls ("traffic lights" on macOS) are visible, this property enables the Window Controls Overlay [JavaScript APIs][overlay-javascript-apis] and [CSS Environment Variables][overlay-css-env-vars]. Specifying `true` will result in an overlay with default system colors. Default is `false`.
|
||||||
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled. Default is the system color.
|
||||||
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled. Default is the system color.
|
||||||
* `height` Integer (optional) _macOS_ _Windows_ - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
* `height` Integer (optional) _macOS_ _Windows_ - The height of the title bar and Window Controls Overlay in pixels. Default is system height.
|
||||||
|
|
||||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
|
||||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
|
||||||
passing a size that does not follow size constraints to `setBounds`/`setSize` or
|
|
||||||
to the constructor of `BrowserWindow`.
|
|
||||||
|
|
||||||
The possible values and behaviors of the `type` option are platform dependent.
|
|
||||||
Possible values are:
|
|
||||||
|
|
||||||
* On Linux, possible types are `desktop`, `dock`, `toolbar`, `splash`,
|
|
||||||
`notification`.
|
|
||||||
* The `desktop` type places the window at the desktop background window level
|
|
||||||
(kCGDesktopWindowLevel - 1). However, note that a desktop window will not
|
|
||||||
receive focus, keyboard, or mouse events. You can still use globalShortcut to
|
|
||||||
receive input sparingly.
|
|
||||||
* The `dock` type creates a dock-like window behavior.
|
|
||||||
* The `toolbar` type creates a window with a toolbar appearance.
|
|
||||||
* The `splash` type behaves in a specific way. It is not
|
|
||||||
draggable, even if the CSS styling of the window's body contains
|
|
||||||
-webkit-app-region: drag. This type is commonly used for splash screens.
|
|
||||||
* The `notification` type creates a window that behaves like a system notification.
|
|
||||||
* On macOS, possible types are `desktop`, `textured`, `panel`.
|
|
||||||
* The `textured` type adds metal gradient appearance
|
|
||||||
(`NSWindowStyleMaskTexturedBackground`).
|
|
||||||
* The `desktop` type places the window at the desktop background window level
|
|
||||||
(`kCGDesktopWindowLevel - 1`). Note that desktop window will not receive
|
|
||||||
focus, keyboard or mouse events, but you can use `globalShortcut` to receive
|
|
||||||
input sparingly.
|
|
||||||
* The `panel` type enables the window to float on top of full-screened apps
|
|
||||||
by adding the `NSWindowStyleMaskNonactivatingPanel` style mask,normally
|
|
||||||
reserved for NSPanel, at runtime. Also, the window will appear on all
|
|
||||||
spaces (desktops).
|
|
||||||
* On Windows, possible type is `toolbar`.
|
|
||||||
|
|
||||||
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
[overlay-css-env-vars]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#css-environment-variables
|
||||||
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
[overlay-javascript-apis]: https://github.com/WICG/window-controls-overlay/blob/main/explainer.md#javascript-apis
|
||||||
|
|
106
docs/api/view.md
Normal file
106
docs/api/view.md
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# View
|
||||||
|
|
||||||
|
> Create and layout native views.
|
||||||
|
|
||||||
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
This module cannot be used until the `ready` event of the `app`
|
||||||
|
module is emitted.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { BaseWindow, View } = require('electron')
|
||||||
|
const win = new BaseWindow()
|
||||||
|
const view = new View()
|
||||||
|
|
||||||
|
view.setBackgroundColor('red')
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 100, height: 100 })
|
||||||
|
win.contentView.addChildView(view)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class: View
|
||||||
|
|
||||||
|
> A basic native view.
|
||||||
|
|
||||||
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
`View` is an [EventEmitter][event-emitter].
|
||||||
|
|
||||||
|
### `new View()`
|
||||||
|
|
||||||
|
Creates a new `View`.
|
||||||
|
|
||||||
|
### Instance Events
|
||||||
|
|
||||||
|
Objects created with `new View` emit the following events:
|
||||||
|
|
||||||
|
#### Event: 'bounds-changed'
|
||||||
|
|
||||||
|
Emitted when the view's bounds have changed in response to being laid out. The
|
||||||
|
new bounds can be retrieved with [`view.getBounds()`](#viewgetbounds).
|
||||||
|
|
||||||
|
### Instance Methods
|
||||||
|
|
||||||
|
Objects created with `new View` have the following instance methods:
|
||||||
|
|
||||||
|
#### `view.addChildView(view[, index])`
|
||||||
|
|
||||||
|
* `view` View - Child view to add.
|
||||||
|
* `index` Integer (optional) - Index at which to insert the child view.
|
||||||
|
Defaults to adding the child at the end of the child list.
|
||||||
|
|
||||||
|
#### `view.removeChildView(view)`
|
||||||
|
|
||||||
|
* `view` View - Child view to remove.
|
||||||
|
|
||||||
|
#### `view.setBounds(bounds)`
|
||||||
|
|
||||||
|
* `bounds` [Rectangle](structures/rectangle.md) - New bounds of the View.
|
||||||
|
|
||||||
|
#### `view.getBounds()`
|
||||||
|
|
||||||
|
Returns [`Rectangle`](structures/rectangle.md) - The bounds of this View, relative to its parent.
|
||||||
|
|
||||||
|
#### `view.setBackgroundColor(color)`
|
||||||
|
|
||||||
|
* `color` string - Color in Hex, RGB, ARGB, HSL, HSLA or named CSS color format. The alpha channel is
|
||||||
|
optional for the hex type.
|
||||||
|
|
||||||
|
Examples of valid `color` values:
|
||||||
|
|
||||||
|
* Hex
|
||||||
|
* `#fff` (RGB)
|
||||||
|
* `#ffff` (ARGB)
|
||||||
|
* `#ffffff` (RRGGBB)
|
||||||
|
* `#ffffffff` (AARRGGBB)
|
||||||
|
* RGB
|
||||||
|
* `rgb\(([\d]+),\s*([\d]+),\s*([\d]+)\)`
|
||||||
|
* e.g. `rgb(255, 255, 255)`
|
||||||
|
* RGBA
|
||||||
|
* `rgba\(([\d]+),\s*([\d]+),\s*([\d]+),\s*([\d.]+)\)`
|
||||||
|
* e.g. `rgba(255, 255, 255, 1.0)`
|
||||||
|
* HSL
|
||||||
|
* `hsl\((-?[\d.]+),\s*([\d.]+)%,\s*([\d.]+)%\)`
|
||||||
|
* e.g. `hsl(200, 20%, 50%)`
|
||||||
|
* HSLA
|
||||||
|
* `hsla\((-?[\d.]+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)`
|
||||||
|
* e.g. `hsla(200, 20%, 50%, 0.5)`
|
||||||
|
* Color name
|
||||||
|
* Options are listed in [SkParseColor.cpp](https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/utils/SkParseColor.cpp;l=11-152;drc=eea4bf52cb0d55e2a39c828b017c80a5ee054148)
|
||||||
|
* Similar to CSS Color Module Level 3 keywords, but case-sensitive.
|
||||||
|
* e.g. `blueviolet` or `red`
|
||||||
|
|
||||||
|
**Note:** Hex format with alpha takes `AARRGGBB` or `ARGB`, _not_ `RRGGBBAA` or `RGB`.
|
||||||
|
|
||||||
|
#### `view.setVisible(visible)`
|
||||||
|
|
||||||
|
* `visible` boolean - If false, the view will be hidden from display.
|
||||||
|
|
||||||
|
### Instance Properties
|
||||||
|
|
||||||
|
Objects created with `new View` have the following properties:
|
||||||
|
|
||||||
|
#### `view.children` _Readonly_
|
||||||
|
|
||||||
|
A `View[]` property representing the child views of this view.
|
||||||
|
|
||||||
|
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
58
docs/api/web-contents-view.md
Normal file
58
docs/api/web-contents-view.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# WebContentsView
|
||||||
|
|
||||||
|
> A View that displays a WebContents.
|
||||||
|
|
||||||
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
This module cannot be used until the `ready` event of the `app`
|
||||||
|
module is emitted.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { BaseWindow, WebContentsView } = require('electron')
|
||||||
|
const win = new BaseWindow({ width: 800, height: 400 })
|
||||||
|
|
||||||
|
const view1 = new WebContentsView()
|
||||||
|
win.contentView.addChildView(view1)
|
||||||
|
view1.webContents.loadURL('https://electronjs.org')
|
||||||
|
view1.setBounds({ x: 0, y: 0, width: 400, height: 400 })
|
||||||
|
|
||||||
|
const view2 = new WebContentsView()
|
||||||
|
win.contentView.addChildView(view2)
|
||||||
|
view2.webContents.loadURL('https://github.com/electron/electron')
|
||||||
|
view2.setBounds({ x: 400, y: 0, width: 400, height: 400 })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class: WebContentsView extends `View`
|
||||||
|
|
||||||
|
> A View that displays a WebContents.
|
||||||
|
|
||||||
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
|
`WebContentsView` inherits from [`View`](view.md).
|
||||||
|
|
||||||
|
`WebContentsView` is an [EventEmitter][event-emitter].
|
||||||
|
|
||||||
|
### `new WebContentsView([options])`
|
||||||
|
|
||||||
|
* `options` Object (optional)
|
||||||
|
* `webPreferences` [WebPreferences](structures/web-preferences.md) (optional) - Settings of web page's features.
|
||||||
|
|
||||||
|
Creates an empty WebContentsView.
|
||||||
|
|
||||||
|
### Instance Properties
|
||||||
|
|
||||||
|
Objects created with `new WebContentsView` have the following properties, in
|
||||||
|
addition to those inherited from [View](view.md):
|
||||||
|
|
||||||
|
#### `view.webContents` _Readonly_
|
||||||
|
|
||||||
|
A `WebContents` property containing a reference to the displayed `WebContents`.
|
||||||
|
Use this to interact with the `WebContents`, for instance to load a URL.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { WebContentsView } = require('electron')
|
||||||
|
const view = new WebContentsView()
|
||||||
|
view.webContents.loadURL('https://electronjs.org/')
|
||||||
|
```
|
||||||
|
|
||||||
|
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
|
@ -4,9 +4,10 @@
|
||||||
|
|
||||||
Electron's `webview` tag is based on [Chromium's `webview`][chrome-webview], which
|
Electron's `webview` tag is based on [Chromium's `webview`][chrome-webview], which
|
||||||
is undergoing dramatic architectural changes. This impacts the stability of `webviews`,
|
is undergoing dramatic architectural changes. This impacts the stability of `webviews`,
|
||||||
including rendering, navigation, and event routing. We currently recommend to not
|
including rendering, navigation, and event routing. We currently recommend to
|
||||||
use the `webview` tag and to consider alternatives, like `iframe`, [Electron's `BrowserView`](browser-view.md),
|
not use the `webview` tag and to consider alternatives, like `iframe`, a
|
||||||
or an architecture that avoids embedded content altogether.
|
[`WebContentsView`](web-contents-view.md), or an architecture that avoids
|
||||||
|
embedded content altogether.
|
||||||
|
|
||||||
## Enabling
|
## Enabling
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,18 @@ app.on('gpu-process-crashed', (event, killed) => { /* ... */ })
|
||||||
app.on('child-process-gone', (event, details) => { /* ... */ })
|
app.on('child-process-gone', (event, details) => { /* ... */ })
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Behavior Changed: `BrowserView.setAutoResize` behavior on macOS
|
||||||
|
|
||||||
|
In Electron 29, BrowserView is now a wrapper around the new [WebContentsView](api/web-contents-view.md) API.
|
||||||
|
|
||||||
|
Previously, the `setAutoResize` function of the `BrowserView` API was backed by [autoresizing](https://developer.apple.com/documentation/appkit/nsview/1483281-autoresizingmask?language=objc) on macOS, and by a custom algorithm on Windows and Linux.
|
||||||
|
For simple use cases such as making a BrowserView fill the entire window, the behavior of these two approaches was identical.
|
||||||
|
However, in more advanced cases, BrowserViews would be autoresized differently on macOS than they would be on other platforms, as the custom resizing algorithm for Windows and Linux did not perfectly match the behavior of macOS's autoresizing API.
|
||||||
|
The autoresizing behavior is now standardized across all platforms.
|
||||||
|
|
||||||
|
If your app uses `BrowserView.setAutoResize` to do anything more complex than making a BrowserView fill the entire window, it's likely you already had custom logic in place to handle this difference in behavior on macOS.
|
||||||
|
If so, that logic will no longer be needed in Electron 29 as autoresizing behavior is consistent.
|
||||||
|
|
||||||
## Planned Breaking API Changes (28.0)
|
## Planned Breaking API Changes (28.0)
|
||||||
|
|
||||||
### Behavior Changed: `WebContents.backgroundThrottling` set to false affects all `WebContents` in the host `BrowserWindow`
|
### Behavior Changed: `WebContents.backgroundThrottling` set to false affects all `WebContents` in the host `BrowserWindow`
|
||||||
|
|
|
@ -79,8 +79,8 @@ will be able to execute native code on the user's machine.
|
||||||
Under no circumstances should you load and execute remote code with
|
Under no circumstances should you load and execute remote code with
|
||||||
Node.js integration enabled. Instead, use only local files (packaged together
|
Node.js integration enabled. Instead, use only local files (packaged together
|
||||||
with your application) to execute Node.js code. To display remote content, use
|
with your application) to execute Node.js code. To display remote content, use
|
||||||
the [`<webview>`][webview-tag] tag or [`BrowserView`][browser-view], make sure
|
the [`<webview>`][webview-tag] tag or a [`WebContentsView`][web-contents-view]
|
||||||
to disable the `nodeIntegration` and enable `contextIsolation`.
|
and make sure to disable the `nodeIntegration` and enable `contextIsolation`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::info Electron security warnings
|
:::info Electron security warnings
|
||||||
|
@ -166,7 +166,7 @@ This recommendation is the default behavior in Electron since 5.0.0.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
It is paramount that you do not enable Node.js integration in any renderer
|
It is paramount that you do not enable Node.js integration in any renderer
|
||||||
([`BrowserWindow`][browser-window], [`BrowserView`][browser-view], or
|
([`BrowserWindow`][browser-window], [`WebContentsView`][web-contents-view], or
|
||||||
[`<webview>`][webview-tag]) that loads remote content. The goal is to limit the
|
[`<webview>`][webview-tag]) that loads remote content. The goal is to limit the
|
||||||
powers you grant to remote content, thus making it dramatically more difficult
|
powers you grant to remote content, thus making it dramatically more difficult
|
||||||
for an attacker to harm your users should they gain the ability to execute
|
for an attacker to harm your users should they gain the ability to execute
|
||||||
|
@ -306,8 +306,8 @@ This recommendation is Electron's default.
|
||||||
|
|
||||||
You may have already guessed that disabling the `webSecurity` property on a
|
You may have already guessed that disabling the `webSecurity` property on a
|
||||||
renderer process ([`BrowserWindow`][browser-window],
|
renderer process ([`BrowserWindow`][browser-window],
|
||||||
[`BrowserView`][browser-view], or [`<webview>`][webview-tag]) disables crucial
|
[`WebContentsView`][web-contents-view], or [`<webview>`][webview-tag]) disables
|
||||||
security features.
|
crucial security features.
|
||||||
|
|
||||||
Do not disable `webSecurity` in production applications.
|
Do not disable `webSecurity` in production applications.
|
||||||
|
|
||||||
|
@ -782,10 +782,10 @@ learn how to serve files / content from a custom protocol.
|
||||||
|
|
||||||
[breaking-changes]: ../breaking-changes.md
|
[breaking-changes]: ../breaking-changes.md
|
||||||
[browser-window]: ../api/browser-window.md
|
[browser-window]: ../api/browser-window.md
|
||||||
[browser-view]: ../api/browser-view.md
|
|
||||||
[webview-tag]: ../api/webview-tag.md
|
[webview-tag]: ../api/webview-tag.md
|
||||||
|
[web-contents-view]: ../api/web-contents-view.md
|
||||||
|
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
||||||
[web-contents]: ../api/web-contents.md
|
[web-contents]: ../api/web-contents.md
|
||||||
[window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
|
[window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
|
||||||
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
||||||
[open-external]: ../api/shell.md#shellopenexternalurl-options
|
[open-external]: ../api/shell.md#shellopenexternalurl-options
|
||||||
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
|
||||||
|
|
|
@ -42,16 +42,15 @@ Compared to an `<iframe>`, `<webview>` tends to be slightly slower but offers
|
||||||
much greater control in loading and communicating with the third-party content
|
much greater control in loading and communicating with the third-party content
|
||||||
and handling various events.
|
and handling various events.
|
||||||
|
|
||||||
### BrowserViews
|
### WebContentsView
|
||||||
|
|
||||||
[BrowserViews](../api/browser-view.md) are not a part of the DOM - instead,
|
[`WebContentsView`](../api/web-contents-view.md)s are not a part of the
|
||||||
they are created in and controlled by your Main process. They are simply
|
DOM—instead, they are created, controlled, positioned, and sized by your
|
||||||
another layer of web content on top of your existing window. This means
|
Main process. Using `WebContentsView`, you can combine and layer many pages
|
||||||
that they are completely separate from your own `BrowserWindow` content and
|
together in the same [`BaseWindow`](../api/base-window.md).
|
||||||
their position is not controlled by the DOM or CSS. Instead, it is controlled
|
|
||||||
by setting the bounds in the Main process.
|
|
||||||
|
|
||||||
`BrowserViews` offer the greatest control over their contents, since they
|
`WebContentsView`s offer the greatest control over their contents, since they
|
||||||
implement the `webContents` similarly to how the `BrowserWindow` does it.
|
implement the `webContents` similarly to how `BrowserWindow` does it. However,
|
||||||
However, as `BrowserViews` are not a part of your DOM, but are rather overlaid
|
as `WebContentsView`s are not elements inside the DOM, positioning them
|
||||||
on top of them, you will have to manage their position manually.
|
accurately with respect to DOM content requires coordination between the
|
||||||
|
Main and Renderer processes.
|
||||||
|
|
|
@ -4,6 +4,7 @@ auto_filenames = {
|
||||||
"docs/api/accelerator.md",
|
"docs/api/accelerator.md",
|
||||||
"docs/api/app.md",
|
"docs/api/app.md",
|
||||||
"docs/api/auto-updater.md",
|
"docs/api/auto-updater.md",
|
||||||
|
"docs/api/base-window.md",
|
||||||
"docs/api/browser-view.md",
|
"docs/api/browser-view.md",
|
||||||
"docs/api/browser-window.md",
|
"docs/api/browser-window.md",
|
||||||
"docs/api/client-request.md",
|
"docs/api/client-request.md",
|
||||||
|
@ -63,6 +64,8 @@ auto_filenames = {
|
||||||
"docs/api/touch-bar.md",
|
"docs/api/touch-bar.md",
|
||||||
"docs/api/tray.md",
|
"docs/api/tray.md",
|
||||||
"docs/api/utility-process.md",
|
"docs/api/utility-process.md",
|
||||||
|
"docs/api/view.md",
|
||||||
|
"docs/api/web-contents-view.md",
|
||||||
"docs/api/web-contents.md",
|
"docs/api/web-contents.md",
|
||||||
"docs/api/web-frame-main.md",
|
"docs/api/web-frame-main.md",
|
||||||
"docs/api/web-frame.md",
|
"docs/api/web-frame.md",
|
||||||
|
@ -70,6 +73,7 @@ auto_filenames = {
|
||||||
"docs/api/web-utils.md",
|
"docs/api/web-utils.md",
|
||||||
"docs/api/webview-tag.md",
|
"docs/api/webview-tag.md",
|
||||||
"docs/api/window-open.md",
|
"docs/api/window-open.md",
|
||||||
|
"docs/api/structures/base-window-options.md",
|
||||||
"docs/api/structures/bluetooth-device.md",
|
"docs/api/structures/bluetooth-device.md",
|
||||||
"docs/api/structures/browser-window-options.md",
|
"docs/api/structures/browser-window-options.md",
|
||||||
"docs/api/structures/certificate-principal.md",
|
"docs/api/structures/certificate-principal.md",
|
||||||
|
|
|
@ -118,6 +118,8 @@ filenames = {
|
||||||
lib_sources_mac = [
|
lib_sources_mac = [
|
||||||
"shell/app/electron_main_delegate_mac.h",
|
"shell/app/electron_main_delegate_mac.h",
|
||||||
"shell/app/electron_main_delegate_mac.mm",
|
"shell/app/electron_main_delegate_mac.mm",
|
||||||
|
"shell/browser/animation_util.h",
|
||||||
|
"shell/browser/animation_util_mac.mm",
|
||||||
"shell/browser/api/electron_api_app_mac.mm",
|
"shell/browser/api/electron_api_app_mac.mm",
|
||||||
"shell/browser/api/electron_api_menu_mac.h",
|
"shell/browser/api/electron_api_menu_mac.h",
|
||||||
"shell/browser/api/electron_api_menu_mac.mm",
|
"shell/browser/api/electron_api_menu_mac.mm",
|
||||||
|
@ -141,8 +143,6 @@ filenames = {
|
||||||
"shell/browser/mac/in_app_purchase_product.mm",
|
"shell/browser/mac/in_app_purchase_product.mm",
|
||||||
"shell/browser/mac/in_app_purchase.h",
|
"shell/browser/mac/in_app_purchase.h",
|
||||||
"shell/browser/mac/in_app_purchase.mm",
|
"shell/browser/mac/in_app_purchase.mm",
|
||||||
"shell/browser/native_browser_view_mac.h",
|
|
||||||
"shell/browser/native_browser_view_mac.mm",
|
|
||||||
"shell/browser/native_window_mac.h",
|
"shell/browser/native_window_mac.h",
|
||||||
"shell/browser/native_window_mac.mm",
|
"shell/browser/native_window_mac.mm",
|
||||||
"shell/browser/notifications/mac/cocoa_notification.h",
|
"shell/browser/notifications/mac/cocoa_notification.h",
|
||||||
|
@ -209,8 +209,6 @@ filenames = {
|
||||||
lib_sources_views = [
|
lib_sources_views = [
|
||||||
"shell/browser/api/electron_api_menu_views.cc",
|
"shell/browser/api/electron_api_menu_views.cc",
|
||||||
"shell/browser/api/electron_api_menu_views.h",
|
"shell/browser/api/electron_api_menu_views.h",
|
||||||
"shell/browser/native_browser_view_views.cc",
|
|
||||||
"shell/browser/native_browser_view_views.h",
|
|
||||||
"shell/browser/native_window_views.cc",
|
"shell/browser/native_window_views.cc",
|
||||||
"shell/browser/native_window_views.h",
|
"shell/browser/native_window_views.h",
|
||||||
"shell/browser/ui/drag_util_views.cc",
|
"shell/browser/ui/drag_util_views.cc",
|
||||||
|
@ -255,8 +253,6 @@ filenames = {
|
||||||
"shell/browser/api/electron_api_auto_updater.h",
|
"shell/browser/api/electron_api_auto_updater.h",
|
||||||
"shell/browser/api/electron_api_base_window.cc",
|
"shell/browser/api/electron_api_base_window.cc",
|
||||||
"shell/browser/api/electron_api_base_window.h",
|
"shell/browser/api/electron_api_base_window.h",
|
||||||
"shell/browser/api/electron_api_browser_view.cc",
|
|
||||||
"shell/browser/api/electron_api_browser_view.h",
|
|
||||||
"shell/browser/api/electron_api_browser_window.cc",
|
"shell/browser/api/electron_api_browser_window.cc",
|
||||||
"shell/browser/api/electron_api_browser_window.h",
|
"shell/browser/api/electron_api_browser_window.h",
|
||||||
"shell/browser/api/electron_api_content_tracing.cc",
|
"shell/browser/api/electron_api_content_tracing.cc",
|
||||||
|
@ -339,6 +335,8 @@ filenames = {
|
||||||
"shell/browser/api/save_page_handler.h",
|
"shell/browser/api/save_page_handler.h",
|
||||||
"shell/browser/api/ui_event.cc",
|
"shell/browser/api/ui_event.cc",
|
||||||
"shell/browser/api/ui_event.h",
|
"shell/browser/api/ui_event.h",
|
||||||
|
"shell/browser/api/views/electron_api_image_view.cc",
|
||||||
|
"shell/browser/api/views/electron_api_image_view.h",
|
||||||
"shell/browser/auto_updater.cc",
|
"shell/browser/auto_updater.cc",
|
||||||
"shell/browser/auto_updater.h",
|
"shell/browser/auto_updater.h",
|
||||||
"shell/browser/background_throttling_source.h",
|
"shell/browser/background_throttling_source.h",
|
||||||
|
@ -413,8 +411,6 @@ filenames = {
|
||||||
"shell/browser/media/media_device_id_salt.h",
|
"shell/browser/media/media_device_id_salt.h",
|
||||||
"shell/browser/microtasks_runner.cc",
|
"shell/browser/microtasks_runner.cc",
|
||||||
"shell/browser/microtasks_runner.h",
|
"shell/browser/microtasks_runner.h",
|
||||||
"shell/browser/native_browser_view.cc",
|
|
||||||
"shell/browser/native_browser_view.h",
|
|
||||||
"shell/browser/native_window.cc",
|
"shell/browser/native_window.cc",
|
||||||
"shell/browser/native_window.h",
|
"shell/browser/native_window.h",
|
||||||
"shell/browser/native_window_features.cc",
|
"shell/browser/native_window_features.cc",
|
||||||
|
|
|
@ -4,7 +4,7 @@ const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as
|
||||||
|
|
||||||
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||||
|
|
||||||
BaseWindow.prototype._init = function () {
|
BaseWindow.prototype._init = function (this: TLWT) {
|
||||||
// Avoid recursive require.
|
// Avoid recursive require.
|
||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ Object.defineProperty(BaseWindow.prototype, 'movable', {
|
||||||
});
|
});
|
||||||
|
|
||||||
BaseWindow.getFocusedWindow = () => {
|
BaseWindow.getFocusedWindow = () => {
|
||||||
return BaseWindow.getAllWindows().find((win) => win.isFocused());
|
return BaseWindow.getAllWindows().find((win) => win.isFocused()) ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = BaseWindow;
|
module.exports = BaseWindow;
|
||||||
|
|
|
@ -1,3 +1,120 @@
|
||||||
const { BrowserView } = process._linkedBinding('electron_browser_browser_view');
|
import { BrowserWindow, AutoResizeOptions, Rectangle, WebContentsView, WebPreferences, WebContents } from 'electron/main';
|
||||||
|
|
||||||
export default BrowserView;
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||||
|
|
||||||
|
export default class BrowserView {
|
||||||
|
#webContentsView: WebContentsView;
|
||||||
|
|
||||||
|
// AutoResize state
|
||||||
|
#resizeListener: ((...args: any[]) => void) | null = null;
|
||||||
|
#lastWindowSize: {width: number, height: number} = { width: 0, height: 0 };
|
||||||
|
#autoResizeFlags: AutoResizeOptions = {};
|
||||||
|
|
||||||
|
constructor (options: {webPreferences: WebPreferences, webContents?: WebContents} = { webPreferences: {} }) {
|
||||||
|
const { webPreferences = {}, webContents } = options;
|
||||||
|
if (webContents) {
|
||||||
|
v8Util.setHiddenValue(webPreferences, 'webContents', webContents);
|
||||||
|
}
|
||||||
|
webPreferences.type = 'browserView';
|
||||||
|
this.#webContentsView = new WebContentsView({ webPreferences });
|
||||||
|
}
|
||||||
|
|
||||||
|
get webContents () {
|
||||||
|
return this.#webContentsView.webContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBounds (bounds: Rectangle) {
|
||||||
|
this.#webContentsView.setBounds(bounds);
|
||||||
|
this.#autoHorizontalProportion = null;
|
||||||
|
this.#autoVerticalProportion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBounds () {
|
||||||
|
return this.#webContentsView.getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
setAutoResize (options: AutoResizeOptions) {
|
||||||
|
if (options == null || typeof options !== 'object') { throw new Error('Invalid auto resize options'); }
|
||||||
|
this.#autoResizeFlags = {
|
||||||
|
width: !!options.width,
|
||||||
|
height: !!options.height,
|
||||||
|
horizontal: !!options.horizontal,
|
||||||
|
vertical: !!options.vertical
|
||||||
|
};
|
||||||
|
this.#autoHorizontalProportion = null;
|
||||||
|
this.#autoVerticalProportion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBackgroundColor (color: string) {
|
||||||
|
this.#webContentsView.setBackgroundColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal methods
|
||||||
|
get ownerWindow (): BrowserWindow | null {
|
||||||
|
return !this.webContents.isDestroyed() ? this.webContents.getOwnerBrowserWindow() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
set ownerWindow (w: BrowserWindow | null) {
|
||||||
|
if (this.webContents.isDestroyed()) return;
|
||||||
|
const oldWindow = this.webContents.getOwnerBrowserWindow();
|
||||||
|
if (oldWindow && this.#resizeListener) {
|
||||||
|
oldWindow.off('resize', this.#resizeListener);
|
||||||
|
this.#resizeListener = null;
|
||||||
|
}
|
||||||
|
this.webContents._setOwnerWindow(w);
|
||||||
|
if (w) {
|
||||||
|
this.#lastWindowSize = w.getBounds();
|
||||||
|
w.on('resize', this.#resizeListener = this.#autoResize.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#autoHorizontalProportion: {width: number, left: number} | null = null;
|
||||||
|
#autoVerticalProportion: {height: number, top: number} | null = null;
|
||||||
|
#autoResize () {
|
||||||
|
if (!this.ownerWindow) throw new Error('Electron bug: #autoResize called without owner window');
|
||||||
|
if (this.#autoResizeFlags.horizontal && this.#autoHorizontalProportion == null) {
|
||||||
|
const viewBounds = this.#webContentsView.getBounds();
|
||||||
|
this.#autoHorizontalProportion = {
|
||||||
|
width: this.#lastWindowSize.width / viewBounds.width,
|
||||||
|
left: this.#lastWindowSize.width / viewBounds.x
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.#autoResizeFlags.vertical && this.#autoVerticalProportion == null) {
|
||||||
|
const viewBounds = this.#webContentsView.getBounds();
|
||||||
|
this.#autoVerticalProportion = {
|
||||||
|
height: this.#lastWindowSize.height / viewBounds.height,
|
||||||
|
top: this.#lastWindowSize.height / viewBounds.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const newBounds = this.ownerWindow.getBounds();
|
||||||
|
let widthDelta = newBounds.width - this.#lastWindowSize.width;
|
||||||
|
let heightDelta = newBounds.height - this.#lastWindowSize.height;
|
||||||
|
if (!this.#autoResizeFlags.width) widthDelta = 0;
|
||||||
|
if (!this.#autoResizeFlags.height) heightDelta = 0;
|
||||||
|
|
||||||
|
const newViewBounds = this.#webContentsView.getBounds();
|
||||||
|
if (widthDelta || heightDelta) {
|
||||||
|
this.#webContentsView.setBounds({
|
||||||
|
...newViewBounds,
|
||||||
|
width: newViewBounds.width + widthDelta,
|
||||||
|
height: newViewBounds.height + heightDelta
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#autoHorizontalProportion) {
|
||||||
|
newViewBounds.width = newBounds.width / this.#autoHorizontalProportion.width;
|
||||||
|
newViewBounds.x = newBounds.width / this.#autoHorizontalProportion.left;
|
||||||
|
}
|
||||||
|
if (this.#autoVerticalProportion) {
|
||||||
|
newViewBounds.height = newBounds.height / this.#autoVerticalProportion.height;
|
||||||
|
newViewBounds.y = newBounds.y / this.#autoVerticalProportion.top;
|
||||||
|
}
|
||||||
|
if (this.#autoHorizontalProportion || this.#autoVerticalProportion) {
|
||||||
|
this.#webContentsView.setBounds(newViewBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get webContentsView () {
|
||||||
|
return this.#webContentsView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { BaseWindow, WebContents, BrowserView, TouchBar } from 'electron/main';
|
import { BaseWindow, WebContents, TouchBar, BrowserView } from 'electron/main';
|
||||||
import type { BrowserWindow as BWT } from 'electron/main';
|
import type { BrowserWindow as BWT } from 'electron/main';
|
||||||
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
|
||||||
|
|
||||||
BrowserWindow.prototype._init = function (this: BWT) {
|
BrowserWindow.prototype._init = function (this: BWT) {
|
||||||
// Call parent class's _init.
|
// Call parent class's _init.
|
||||||
BaseWindow.prototype._init.call(this);
|
(BaseWindow.prototype as any)._init.call(this);
|
||||||
|
|
||||||
// Avoid recursive require.
|
// Avoid recursive require.
|
||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
|
@ -52,6 +52,12 @@ BrowserWindow.prototype._init = function (this: BWT) {
|
||||||
this.on(event as any, visibilityChanged);
|
this.on(event as any, visibilityChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._browserViews = [];
|
||||||
|
|
||||||
|
this.on('close', () => {
|
||||||
|
this._browserViews.forEach(b => b.webContents?.close({ waitForBeforeUnload: true }));
|
||||||
|
});
|
||||||
|
|
||||||
// Notify the creation of the window.
|
// Notify the creation of the window.
|
||||||
app.emit('browser-window-created', { preventDefault () {} }, this);
|
app.emit('browser-window-created', { preventDefault () {} }, this);
|
||||||
|
|
||||||
|
@ -168,4 +174,42 @@ BrowserWindow.prototype.setBackgroundThrottling = function (allowed: boolean) {
|
||||||
return this.webContents.setBackgroundThrottling(allowed);
|
return this.webContents.setBackgroundThrottling(allowed);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.addBrowserView = function (browserView: BrowserView) {
|
||||||
|
if (browserView.ownerWindow) { browserView.ownerWindow.removeBrowserView(browserView); }
|
||||||
|
this.contentView.addChildView(browserView.webContentsView);
|
||||||
|
browserView.ownerWindow = this;
|
||||||
|
browserView.webContents._setOwnerWindow(this);
|
||||||
|
this._browserViews.push(browserView);
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.setBrowserView = function (browserView: BrowserView) {
|
||||||
|
this._browserViews.forEach(bv => {
|
||||||
|
this.removeBrowserView(bv);
|
||||||
|
});
|
||||||
|
if (browserView) { this.addBrowserView(browserView); }
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.removeBrowserView = function (browserView: BrowserView) {
|
||||||
|
const idx = this._browserViews.indexOf(browserView);
|
||||||
|
if (idx >= 0) {
|
||||||
|
this.contentView.removeChildView(browserView.webContentsView);
|
||||||
|
browserView.ownerWindow = null;
|
||||||
|
this._browserViews.splice(idx, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.getBrowserView = function () {
|
||||||
|
if (this._browserViews.length > 1) { throw new Error('This BrowserWindow has multiple BrowserViews, use getBrowserViews() instead'); }
|
||||||
|
return this._browserViews[0] ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.getBrowserViews = function () {
|
||||||
|
return [...this._browserViews];
|
||||||
|
};
|
||||||
|
|
||||||
|
BrowserWindow.prototype.setTopBrowserView = function (browserView: BrowserView) {
|
||||||
|
if (browserView.ownerWindow !== this) { throw new Error('Given BrowserView is not attached to the window'); }
|
||||||
|
this.addBrowserView(browserView);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = BrowserWindow;
|
module.exports = BrowserWindow;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{ name: 'dialog', loader: () => require('./dialog') },
|
{ name: 'dialog', loader: () => require('./dialog') },
|
||||||
{ name: 'globalShortcut', loader: () => require('./global-shortcut') },
|
{ name: 'globalShortcut', loader: () => require('./global-shortcut') },
|
||||||
{ name: 'ipcMain', loader: () => require('./ipc-main') },
|
{ name: 'ipcMain', loader: () => require('./ipc-main') },
|
||||||
|
{ name: 'ImageView', loader: () => require('./views/image-view') },
|
||||||
{ name: 'inAppPurchase', loader: () => require('./in-app-purchase') },
|
{ name: 'inAppPurchase', loader: () => require('./in-app-purchase') },
|
||||||
{ name: 'Menu', loader: () => require('./menu') },
|
{ name: 'Menu', loader: () => require('./menu') },
|
||||||
{ name: 'MenuItem', loader: () => require('./menu-item') },
|
{ name: 'MenuItem', loader: () => require('./menu-item') },
|
||||||
|
@ -39,9 +40,3 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{ name: 'WebContentsView', loader: () => require('./web-contents-view') },
|
{ name: 'WebContentsView', loader: () => require('./web-contents-view') },
|
||||||
{ name: 'webFrameMain', loader: () => require('./web-frame-main') }
|
{ name: 'webFrameMain', loader: () => require('./web-frame-main') }
|
||||||
];
|
];
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_VIEWS_API)) {
|
|
||||||
browserModuleList.push(
|
|
||||||
{ name: 'ImageView', loader: () => require('./views/image-view') }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
const { View } = process._linkedBinding('electron_browser_view');
|
const { View } = process._linkedBinding('electron_browser_view');
|
||||||
|
|
||||||
|
Object.setPrototypeOf((View as any).prototype, EventEmitter.prototype);
|
||||||
|
|
||||||
export default View;
|
export default View;
|
||||||
|
|
|
@ -592,6 +592,16 @@ WebContents.prototype._init = function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.on('-before-unload-fired' as any, function (this: Electron.WebContents, event: Electron.Event, proceed: boolean) {
|
||||||
|
const type = this.getType();
|
||||||
|
// These are the "interactive" types, i.e. ones a user might be looking at.
|
||||||
|
// All other types should ignore the "proceed" signal and unload
|
||||||
|
// regardless.
|
||||||
|
if (type === 'window' || type === 'offscreen' || type === 'browserView') {
|
||||||
|
if (!proceed) { return event.preventDefault(); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// The devtools requests the webContents to reload.
|
// The devtools requests the webContents to reload.
|
||||||
this.on('devtools-reload-page', function (this: Electron.WebContents) {
|
this.on('devtools-reload-page', function (this: Electron.WebContents) {
|
||||||
this.reload();
|
this.reload();
|
||||||
|
@ -776,7 +786,7 @@ WebContents.prototype._init = function () {
|
||||||
const promise = parent && !prefs.offscreen ? dialog.showMessageBox(parent, options) : dialog.showMessageBox(options);
|
const promise = parent && !prefs.offscreen ? dialog.showMessageBox(parent, options) : dialog.showMessageBox(options);
|
||||||
try {
|
try {
|
||||||
const result = await promise;
|
const result = await promise;
|
||||||
if (abortController.signal.aborted) return;
|
if (abortController.signal.aborted || this.isDestroyed()) return;
|
||||||
if (result.checkboxChecked) originCounts.set(origin, -1);
|
if (result.checkboxChecked) originCounts.set(origin, -1);
|
||||||
return callback(result.response === 0, '');
|
return callback(result.response === 0, '');
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -152,6 +152,10 @@ require('@electron/internal/browser/api/web-contents');
|
||||||
// Load web-frame-main module to ensure it is populated on app ready
|
// Load web-frame-main module to ensure it is populated on app ready
|
||||||
require('@electron/internal/browser/api/web-frame-main');
|
require('@electron/internal/browser/api/web-frame-main');
|
||||||
|
|
||||||
|
// Required because `new BrowserWindow` calls some WebContentsView stuff, so
|
||||||
|
// the inheritance needs to be set up before that happens.
|
||||||
|
require('@electron/internal/browser/api/web-contents-view');
|
||||||
|
|
||||||
// Set main startup script of the app.
|
// Set main startup script of the app.
|
||||||
const mainStartupScript = packageJson.main || 'index.js';
|
const mainStartupScript = packageJson.main || 'index.js';
|
||||||
|
|
||||||
|
|
18
shell/browser/animation_util.h
Normal file
18
shell/browser/animation_util.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) 2022 Salesforce, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ELECTRON_SHELL_BROWSER_ANIMATION_UTIL_H_
|
||||||
|
#define ELECTRON_SHELL_BROWSER_ANIMATION_UTIL_H_
|
||||||
|
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
class ScopedCAActionDisabler {
|
||||||
|
public:
|
||||||
|
ScopedCAActionDisabler();
|
||||||
|
~ScopedCAActionDisabler();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ELECTRON_SHELL_BROWSER_ANIMATION_UTIL_H_
|
17
shell/browser/animation_util_mac.mm
Normal file
17
shell/browser/animation_util_mac.mm
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) 2022 Salesforce, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "shell/browser/animation_util.h"
|
||||||
|
|
||||||
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
|
||||||
|
// Disables actions within a scope.
|
||||||
|
ScopedCAActionDisabler::ScopedCAActionDisabler() {
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setDisableActions:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedCAActionDisabler::~ScopedCAActionDisabler() {
|
||||||
|
[CATransaction commit];
|
||||||
|
}
|
|
@ -13,7 +13,6 @@
|
||||||
#include "base/task/single_thread_task_runner.h"
|
#include "base/task/single_thread_task_runner.h"
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
#include "gin/dictionary.h"
|
#include "gin/dictionary.h"
|
||||||
#include "shell/browser/api/electron_api_browser_view.h"
|
|
||||||
#include "shell/browser/api/electron_api_menu.h"
|
#include "shell/browser/api/electron_api_menu.h"
|
||||||
#include "shell/browser/api/electron_api_view.h"
|
#include "shell/browser/api/electron_api_view.h"
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
#include "shell/browser/api/electron_api_web_contents.h"
|
||||||
|
@ -97,6 +96,8 @@ BaseWindow::BaseWindow(v8::Isolate* isolate,
|
||||||
options, parent.IsEmpty() ? nullptr : parent->window_.get()));
|
options, parent.IsEmpty() ? nullptr : parent->window_.get()));
|
||||||
window_->AddObserver(this);
|
window_->AddObserver(this);
|
||||||
|
|
||||||
|
SetContentView(View::Create(isolate));
|
||||||
|
|
||||||
#if defined(TOOLKIT_VIEWS)
|
#if defined(TOOLKIT_VIEWS)
|
||||||
v8::Local<v8::Value> icon;
|
v8::Local<v8::Value> icon;
|
||||||
if (options.Get(options::kIcon, &icon)) {
|
if (options.Get(options::kIcon, &icon)) {
|
||||||
|
@ -164,7 +165,6 @@ void BaseWindow::OnWindowClosed() {
|
||||||
Emit("closed");
|
Emit("closed");
|
||||||
|
|
||||||
RemoveFromParentChildWindows();
|
RemoveFromParentChildWindows();
|
||||||
BaseWindow::ResetBrowserViews();
|
|
||||||
|
|
||||||
// Destroy the native class when window is closed.
|
// Destroy the native class when window is closed.
|
||||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
||||||
|
@ -311,8 +311,7 @@ void BaseWindow::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void BaseWindow::SetContentView(gin::Handle<View> view) {
|
void BaseWindow::SetContentView(gin::Handle<View> view) {
|
||||||
ResetBrowserViews();
|
content_view_.Reset(JavascriptEnvironment::GetIsolate(), view.ToV8());
|
||||||
content_view_.Reset(isolate(), view.ToV8());
|
|
||||||
window_->SetContentView(view->view());
|
window_->SetContentView(view->view());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,70 +748,6 @@ void BaseWindow::SetParentWindow(v8::Local<v8::Value> value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseWindow::SetBrowserView(
|
|
||||||
absl::optional<gin::Handle<BrowserView>> browser_view) {
|
|
||||||
ResetBrowserViews();
|
|
||||||
if (browser_view)
|
|
||||||
AddBrowserView(*browser_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseWindow::AddBrowserView(gin::Handle<BrowserView> browser_view) {
|
|
||||||
if (!base::Contains(browser_views_, browser_view.ToV8())) {
|
|
||||||
// If we're reparenting a BrowserView, ensure that it's detached from
|
|
||||||
// its previous owner window.
|
|
||||||
BaseWindow* owner_window = browser_view->owner_window();
|
|
||||||
if (owner_window) {
|
|
||||||
// iter == browser_views_.end() should imply owner_window != this.
|
|
||||||
DCHECK_NE(owner_window, this);
|
|
||||||
owner_window->RemoveBrowserView(browser_view);
|
|
||||||
browser_view->SetOwnerWindow(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user set the BrowserView's bounds before adding it to the window,
|
|
||||||
// we need to get those initial bounds *before* adding it to the window
|
|
||||||
// so bounds isn't returned relative despite not being correctly positioned
|
|
||||||
// relative to the window.
|
|
||||||
auto bounds = browser_view->GetBounds();
|
|
||||||
|
|
||||||
window_->AddBrowserView(browser_view->view());
|
|
||||||
window_->AddDraggableRegionProvider(browser_view.get());
|
|
||||||
browser_view->SetOwnerWindow(this);
|
|
||||||
browser_views_.emplace_back().Reset(isolate(), browser_view.ToV8());
|
|
||||||
|
|
||||||
// Recalibrate bounds relative to the containing window.
|
|
||||||
if (!bounds.IsEmpty())
|
|
||||||
browser_view->SetBounds(bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseWindow::RemoveBrowserView(gin::Handle<BrowserView> browser_view) {
|
|
||||||
auto iter = std::find(browser_views_.begin(), browser_views_.end(),
|
|
||||||
browser_view.ToV8());
|
|
||||||
|
|
||||||
if (iter != browser_views_.end()) {
|
|
||||||
window_->RemoveDraggableRegionProvider(browser_view.get());
|
|
||||||
window_->RemoveBrowserView(browser_view->view());
|
|
||||||
browser_view->SetOwnerWindow(nullptr);
|
|
||||||
iter->Reset();
|
|
||||||
browser_views_.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseWindow::SetTopBrowserView(gin::Handle<BrowserView> browser_view,
|
|
||||||
gin_helper::Arguments* args) {
|
|
||||||
BaseWindow* owner_window = browser_view->owner_window();
|
|
||||||
auto iter = std::find(browser_views_.begin(), browser_views_.end(),
|
|
||||||
browser_view.ToV8());
|
|
||||||
if (iter == browser_views_.end() || (owner_window && owner_window != this)) {
|
|
||||||
args->ThrowError("Given BrowserView is not attached to the window");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_views_.erase(iter);
|
|
||||||
browser_views_.emplace_back().Reset(isolate(), browser_view.ToV8());
|
|
||||||
window_->SetTopBrowserView(browser_view->view());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string BaseWindow::GetMediaSourceId() const {
|
std::string BaseWindow::GetMediaSourceId() const {
|
||||||
return window_->GetDesktopMediaID().ToString();
|
return window_->GetDesktopMediaID().ToString();
|
||||||
}
|
}
|
||||||
|
@ -1018,31 +953,6 @@ std::vector<v8::Local<v8::Object>> BaseWindow::GetChildWindows() const {
|
||||||
return child_windows_.Values(isolate());
|
return child_windows_.Values(isolate());
|
||||||
}
|
}
|
||||||
|
|
||||||
v8::Local<v8::Value> BaseWindow::GetBrowserView(
|
|
||||||
gin_helper::Arguments* args) const {
|
|
||||||
if (browser_views_.empty()) {
|
|
||||||
return v8::Null(isolate());
|
|
||||||
} else if (browser_views_.size() == 1) {
|
|
||||||
auto first_view = browser_views_.begin();
|
|
||||||
return v8::Local<v8::Value>::New(isolate(), *first_view);
|
|
||||||
} else {
|
|
||||||
args->ThrowError(
|
|
||||||
"BrowserWindow have multiple BrowserViews, "
|
|
||||||
"Use getBrowserViews() instead");
|
|
||||||
return v8::Null(isolate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<v8::Local<v8::Value>> BaseWindow::GetBrowserViews() const {
|
|
||||||
std::vector<v8::Local<v8::Value>> ret;
|
|
||||||
|
|
||||||
for (auto const& browser_view : browser_views_) {
|
|
||||||
ret.push_back(v8::Local<v8::Value>::New(isolate(), browser_view));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseWindow::IsModal() const {
|
bool BaseWindow::IsModal() const {
|
||||||
return window_->is_modal();
|
return window_->is_modal();
|
||||||
}
|
}
|
||||||
|
@ -1140,31 +1050,6 @@ int32_t BaseWindow::GetID() const {
|
||||||
return weak_map_id();
|
return weak_map_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseWindow::ResetBrowserViews() {
|
|
||||||
v8::HandleScope scope(isolate());
|
|
||||||
|
|
||||||
for (auto& item : browser_views_) {
|
|
||||||
gin::Handle<BrowserView> browser_view;
|
|
||||||
if (gin::ConvertFromV8(isolate(),
|
|
||||||
v8::Local<v8::Value>::New(isolate(), item),
|
|
||||||
&browser_view) &&
|
|
||||||
!browser_view.IsEmpty()) {
|
|
||||||
// There's a chance that the BrowserView may have been reparented - only
|
|
||||||
// reset if the owner window is *this* window.
|
|
||||||
BaseWindow* owner_window = browser_view->owner_window();
|
|
||||||
DCHECK_EQ(owner_window, this);
|
|
||||||
browser_view->SetOwnerWindow(nullptr);
|
|
||||||
window_->RemoveBrowserView(browser_view->view());
|
|
||||||
window_->RemoveDraggableRegionProvider(browser_view.get());
|
|
||||||
browser_view->SetOwnerWindow(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
browser_views_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BaseWindow::RemoveFromParentChildWindows() {
|
void BaseWindow::RemoveFromParentChildWindows() {
|
||||||
if (parent_window_.IsEmpty())
|
if (parent_window_.IsEmpty())
|
||||||
return;
|
return;
|
||||||
|
@ -1274,10 +1159,6 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("setMenu", &BaseWindow::SetMenu)
|
.SetMethod("setMenu", &BaseWindow::SetMenu)
|
||||||
.SetMethod("removeMenu", &BaseWindow::RemoveMenu)
|
.SetMethod("removeMenu", &BaseWindow::RemoveMenu)
|
||||||
.SetMethod("setParentWindow", &BaseWindow::SetParentWindow)
|
.SetMethod("setParentWindow", &BaseWindow::SetParentWindow)
|
||||||
.SetMethod("setBrowserView", &BaseWindow::SetBrowserView)
|
|
||||||
.SetMethod("addBrowserView", &BaseWindow::AddBrowserView)
|
|
||||||
.SetMethod("removeBrowserView", &BaseWindow::RemoveBrowserView)
|
|
||||||
.SetMethod("setTopBrowserView", &BaseWindow::SetTopBrowserView)
|
|
||||||
.SetMethod("getMediaSourceId", &BaseWindow::GetMediaSourceId)
|
.SetMethod("getMediaSourceId", &BaseWindow::GetMediaSourceId)
|
||||||
.SetMethod("getNativeWindowHandle", &BaseWindow::GetNativeWindowHandle)
|
.SetMethod("getNativeWindowHandle", &BaseWindow::GetNativeWindowHandle)
|
||||||
.SetMethod("setProgressBar", &BaseWindow::SetProgressBar)
|
.SetMethod("setProgressBar", &BaseWindow::SetProgressBar)
|
||||||
|
@ -1333,10 +1214,10 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("previewFile", &BaseWindow::PreviewFile)
|
.SetMethod("previewFile", &BaseWindow::PreviewFile)
|
||||||
.SetMethod("closeFilePreview", &BaseWindow::CloseFilePreview)
|
.SetMethod("closeFilePreview", &BaseWindow::CloseFilePreview)
|
||||||
.SetMethod("getContentView", &BaseWindow::GetContentView)
|
.SetMethod("getContentView", &BaseWindow::GetContentView)
|
||||||
|
.SetProperty("contentView", &BaseWindow::GetContentView,
|
||||||
|
&BaseWindow::SetContentView)
|
||||||
.SetMethod("getParentWindow", &BaseWindow::GetParentWindow)
|
.SetMethod("getParentWindow", &BaseWindow::GetParentWindow)
|
||||||
.SetMethod("getChildWindows", &BaseWindow::GetChildWindows)
|
.SetMethod("getChildWindows", &BaseWindow::GetChildWindows)
|
||||||
.SetMethod("getBrowserView", &BaseWindow::GetBrowserView)
|
|
||||||
.SetMethod("getBrowserViews", &BaseWindow::GetBrowserViews)
|
|
||||||
.SetMethod("isModal", &BaseWindow::IsModal)
|
.SetMethod("isModal", &BaseWindow::IsModal)
|
||||||
.SetMethod("setThumbarButtons", &BaseWindow::SetThumbarButtons)
|
.SetMethod("setThumbarButtons", &BaseWindow::SetThumbarButtons)
|
||||||
#if defined(TOOLKIT_VIEWS)
|
#if defined(TOOLKIT_VIEWS)
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
namespace electron::api {
|
namespace electron::api {
|
||||||
|
|
||||||
class View;
|
class View;
|
||||||
class BrowserView;
|
|
||||||
|
|
||||||
class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
public NativeWindowObserver {
|
public NativeWindowObserver {
|
||||||
|
@ -173,14 +172,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
void SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> menu);
|
void SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> menu);
|
||||||
void RemoveMenu();
|
void RemoveMenu();
|
||||||
void SetParentWindow(v8::Local<v8::Value> value, gin_helper::Arguments* args);
|
void SetParentWindow(v8::Local<v8::Value> value, gin_helper::Arguments* args);
|
||||||
virtual void SetBrowserView(
|
|
||||||
absl::optional<gin::Handle<BrowserView>> browser_view);
|
|
||||||
virtual void AddBrowserView(gin::Handle<BrowserView> browser_view);
|
|
||||||
virtual void RemoveBrowserView(gin::Handle<BrowserView> browser_view);
|
|
||||||
virtual void SetTopBrowserView(gin::Handle<BrowserView> browser_view,
|
|
||||||
gin_helper::Arguments* args);
|
|
||||||
virtual std::vector<v8::Local<v8::Value>> GetBrowserViews() const;
|
|
||||||
virtual void ResetBrowserViews();
|
|
||||||
std::string GetMediaSourceId() const;
|
std::string GetMediaSourceId() const;
|
||||||
v8::Local<v8::Value> GetNativeWindowHandle();
|
v8::Local<v8::Value> GetNativeWindowHandle();
|
||||||
void SetProgressBar(double progress, gin_helper::Arguments* args);
|
void SetProgressBar(double progress, gin_helper::Arguments* args);
|
||||||
|
@ -227,7 +218,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
v8::Local<v8::Value> GetContentView() const;
|
v8::Local<v8::Value> GetContentView() const;
|
||||||
v8::Local<v8::Value> GetParentWindow() const;
|
v8::Local<v8::Value> GetParentWindow() const;
|
||||||
std::vector<v8::Local<v8::Object>> GetChildWindows() const;
|
std::vector<v8::Local<v8::Object>> GetChildWindows() const;
|
||||||
v8::Local<v8::Value> GetBrowserView(gin_helper::Arguments* args) const;
|
|
||||||
bool IsModal() const;
|
bool IsModal() const;
|
||||||
|
|
||||||
// Extra APIs added in JS.
|
// Extra APIs added in JS.
|
||||||
|
@ -254,9 +244,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
|
|
||||||
// Helpers.
|
// Helpers.
|
||||||
|
|
||||||
// Remove BrowserView.
|
|
||||||
void ResetBrowserView();
|
|
||||||
|
|
||||||
// Remove this window from parent window's |child_windows_|.
|
// Remove this window from parent window's |child_windows_|.
|
||||||
void RemoveFromParentChildWindows();
|
void RemoveFromParentChildWindows();
|
||||||
|
|
||||||
|
@ -274,7 +261,6 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
v8::Global<v8::Value> content_view_;
|
v8::Global<v8::Value> content_view_;
|
||||||
std::vector<v8::Global<v8::Value>> browser_views_;
|
|
||||||
v8::Global<v8::Value> menu_;
|
v8::Global<v8::Value> menu_;
|
||||||
v8::Global<v8::Value> parent_window_;
|
v8::Global<v8::Value> parent_window_;
|
||||||
KeyWeakMap<int> child_windows_;
|
KeyWeakMap<int> child_windows_;
|
||||||
|
|
|
@ -1,231 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "shell/browser/api/electron_api_browser_view.h"
|
|
||||||
|
|
||||||
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
|
|
||||||
#include "content/public/browser/render_widget_host_view.h"
|
|
||||||
#include "shell/browser/api/electron_api_base_window.h"
|
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
|
||||||
#include "shell/browser/browser.h"
|
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
#include "shell/browser/ui/drag_util.h"
|
|
||||||
#include "shell/browser/web_contents_preferences.h"
|
|
||||||
#include "shell/common/color_util.h"
|
|
||||||
#include "shell/common/gin_converters/gfx_converter.h"
|
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
|
||||||
#include "shell/common/node_includes.h"
|
|
||||||
#include "shell/common/options_switches.h"
|
|
||||||
#include "ui/base/hit_test.h"
|
|
||||||
#include "ui/gfx/geometry/rect.h"
|
|
||||||
|
|
||||||
namespace gin {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct Converter<electron::AutoResizeFlags> {
|
|
||||||
static bool FromV8(v8::Isolate* isolate,
|
|
||||||
v8::Local<v8::Value> val,
|
|
||||||
electron::AutoResizeFlags* auto_resize_flags) {
|
|
||||||
gin_helper::Dictionary params;
|
|
||||||
if (!ConvertFromV8(isolate, val, ¶ms)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t flags = 0;
|
|
||||||
bool width = false;
|
|
||||||
if (params.Get("width", &width) && width) {
|
|
||||||
flags |= electron::kAutoResizeWidth;
|
|
||||||
}
|
|
||||||
bool height = false;
|
|
||||||
if (params.Get("height", &height) && height) {
|
|
||||||
flags |= electron::kAutoResizeHeight;
|
|
||||||
}
|
|
||||||
bool horizontal = false;
|
|
||||||
if (params.Get("horizontal", &horizontal) && horizontal) {
|
|
||||||
flags |= electron::kAutoResizeHorizontal;
|
|
||||||
}
|
|
||||||
bool vertical = false;
|
|
||||||
if (params.Get("vertical", &vertical) && vertical) {
|
|
||||||
flags |= electron::kAutoResizeVertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
*auto_resize_flags = static_cast<electron::AutoResizeFlags>(flags);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace gin
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
int32_t GetNextId() {
|
|
||||||
static int32_t next_id = 1;
|
|
||||||
return next_id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace electron::api {
|
|
||||||
|
|
||||||
gin::WrapperInfo BrowserView::kWrapperInfo = {gin::kEmbedderNativeGin};
|
|
||||||
|
|
||||||
BrowserView::BrowserView(gin::Arguments* args,
|
|
||||||
const gin_helper::Dictionary& options)
|
|
||||||
: id_(GetNextId()) {
|
|
||||||
auto web_preferences = gin_helper::Dictionary::CreateEmpty(args->isolate());
|
|
||||||
options.Get(options::kWebPreferences, &web_preferences);
|
|
||||||
web_preferences.Set("type", "browserView");
|
|
||||||
|
|
||||||
v8::Local<v8::Value> value;
|
|
||||||
|
|
||||||
// Copy the webContents option to webPreferences.
|
|
||||||
if (options.Get("webContents", &value)) {
|
|
||||||
web_preferences.SetHidden("webContents", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto web_contents =
|
|
||||||
WebContents::CreateFromWebPreferences(args->isolate(), web_preferences);
|
|
||||||
|
|
||||||
web_contents_.Reset(args->isolate(), web_contents.ToV8());
|
|
||||||
api_web_contents_ = web_contents.get();
|
|
||||||
api_web_contents_->AddObserver(this);
|
|
||||||
Observe(web_contents->web_contents());
|
|
||||||
|
|
||||||
view_.reset(
|
|
||||||
NativeBrowserView::Create(api_web_contents_->inspectable_web_contents()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::SetOwnerWindow(BaseWindow* window) {
|
|
||||||
// Ensure WebContents and BrowserView owner windows are in sync.
|
|
||||||
if (web_contents())
|
|
||||||
web_contents()->SetOwnerWindow(window ? window->window() : nullptr);
|
|
||||||
|
|
||||||
owner_window_ = window ? window->GetWeakPtr() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrowserView::NonClientHitTest(const gfx::Point& point) {
|
|
||||||
gfx::Rect bounds = GetBounds();
|
|
||||||
gfx::Point local_point(point.x() - bounds.x(), point.y() - bounds.y());
|
|
||||||
SkRegion* region = api_web_contents_->draggable_region();
|
|
||||||
if (region && region->contains(local_point.x(), local_point.y()))
|
|
||||||
return HTCAPTION;
|
|
||||||
return HTNOWHERE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowserView::~BrowserView() {
|
|
||||||
if (web_contents()) { // destroy() called without closing WebContents
|
|
||||||
web_contents()->RemoveObserver(this);
|
|
||||||
web_contents()->Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::WebContentsDestroyed() {
|
|
||||||
if (owner_window())
|
|
||||||
owner_window()->window()->RemoveDraggableRegionProvider(this);
|
|
||||||
|
|
||||||
api_web_contents_ = nullptr;
|
|
||||||
web_contents_.Reset();
|
|
||||||
Unpin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::OnCloseContents() {
|
|
||||||
api_web_contents_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
gin::Handle<BrowserView> BrowserView::New(gin_helper::ErrorThrower thrower,
|
|
||||||
gin::Arguments* args) {
|
|
||||||
if (!Browser::Get()->is_ready()) {
|
|
||||||
thrower.ThrowError("Cannot create BrowserView before app is ready");
|
|
||||||
return gin::Handle<BrowserView>();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto options = gin::Dictionary::CreateEmpty(args->isolate());
|
|
||||||
args->GetNext(&options);
|
|
||||||
|
|
||||||
auto handle =
|
|
||||||
gin::CreateHandle(args->isolate(), new BrowserView(args, options));
|
|
||||||
handle->Pin(args->isolate());
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::SetAutoResize(AutoResizeFlags flags) {
|
|
||||||
view_->SetAutoResizeFlags(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::SetBounds(const gfx::Rect& bounds) {
|
|
||||||
view_->SetBounds(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Rect BrowserView::GetBounds() {
|
|
||||||
return view_->GetBounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserView::SetBackgroundColor(const std::string& color_name) {
|
|
||||||
SkColor color = ParseCSSColor(color_name);
|
|
||||||
view_->SetBackgroundColor(color);
|
|
||||||
|
|
||||||
if (web_contents()) {
|
|
||||||
auto* wc = web_contents()->web_contents();
|
|
||||||
wc->SetPageBaseBackgroundColor(ParseCSSColor(color_name));
|
|
||||||
|
|
||||||
auto* const rwhv = wc->GetRenderWidgetHostView();
|
|
||||||
if (rwhv) {
|
|
||||||
rwhv->SetBackgroundColor(color);
|
|
||||||
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
|
|
||||||
->SetContentBackgroundColor(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure new color is stored in webPreferences, otherwise
|
|
||||||
// the color will be reset on the next load via HandleNewRenderFrame.
|
|
||||||
auto* web_preferences = WebContentsPreferences::From(wc);
|
|
||||||
if (web_preferences)
|
|
||||||
web_preferences->SetBackgroundColor(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v8::Local<v8::Value> BrowserView::GetWebContents(v8::Isolate* isolate) {
|
|
||||||
if (web_contents_.IsEmpty()) {
|
|
||||||
return v8::Null(isolate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v8::Local<v8::Value>::New(isolate, web_contents_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void BrowserView::FillObjectTemplate(v8::Isolate* isolate,
|
|
||||||
v8::Local<v8::ObjectTemplate> templ) {
|
|
||||||
gin::ObjectTemplateBuilder(isolate, GetClassName(), templ)
|
|
||||||
.SetMethod("setAutoResize", &BrowserView::SetAutoResize)
|
|
||||||
.SetMethod("setBounds", &BrowserView::SetBounds)
|
|
||||||
.SetMethod("getBounds", &BrowserView::GetBounds)
|
|
||||||
.SetMethod("setBackgroundColor", &BrowserView::SetBackgroundColor)
|
|
||||||
.SetProperty("webContents", &BrowserView::GetWebContents)
|
|
||||||
.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* BrowserView::GetTypeName() {
|
|
||||||
return GetClassName();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace electron::api
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using electron::api::BrowserView;
|
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports,
|
|
||||||
v8::Local<v8::Value> unused,
|
|
||||||
v8::Local<v8::Context> context,
|
|
||||||
void* priv) {
|
|
||||||
v8::Isolate* isolate = context->GetIsolate();
|
|
||||||
|
|
||||||
gin_helper::Dictionary dict(isolate, exports);
|
|
||||||
dict.Set("BrowserView", BrowserView::GetConstructor(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_browser_view, Initialize)
|
|
|
@ -1,98 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_
|
|
||||||
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "base/memory/raw_ptr.h"
|
|
||||||
#include "content/public/browser/web_contents_observer.h"
|
|
||||||
#include "gin/handle.h"
|
|
||||||
#include "gin/wrappable.h"
|
|
||||||
#include "shell/browser/draggable_region_provider.h"
|
|
||||||
#include "shell/browser/extended_web_contents_observer.h"
|
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
#include "shell/browser/native_window.h"
|
|
||||||
#include "shell/common/api/api.mojom.h"
|
|
||||||
#include "shell/common/gin_helper/constructible.h"
|
|
||||||
#include "shell/common/gin_helper/error_thrower.h"
|
|
||||||
#include "shell/common/gin_helper/pinnable.h"
|
|
||||||
|
|
||||||
namespace gfx {
|
|
||||||
class Rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace gin_helper {
|
|
||||||
class Dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace electron::api {
|
|
||||||
|
|
||||||
class WebContents;
|
|
||||||
class BaseWindow;
|
|
||||||
|
|
||||||
class BrowserView : public gin::Wrappable<BrowserView>,
|
|
||||||
public gin_helper::Constructible<BrowserView>,
|
|
||||||
public gin_helper::Pinnable<BrowserView>,
|
|
||||||
public content::WebContentsObserver,
|
|
||||||
public ExtendedWebContentsObserver,
|
|
||||||
public DraggableRegionProvider {
|
|
||||||
public:
|
|
||||||
// gin_helper::Constructible
|
|
||||||
static gin::Handle<BrowserView> New(gin_helper::ErrorThrower thrower,
|
|
||||||
gin::Arguments* args);
|
|
||||||
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>);
|
|
||||||
static const char* GetClassName() { return "BrowserView"; }
|
|
||||||
|
|
||||||
// gin::Wrappable
|
|
||||||
static gin::WrapperInfo kWrapperInfo;
|
|
||||||
const char* GetTypeName() override;
|
|
||||||
|
|
||||||
WebContents* web_contents() const { return api_web_contents_; }
|
|
||||||
NativeBrowserView* view() const { return view_.get(); }
|
|
||||||
|
|
||||||
BaseWindow* owner_window() const { return owner_window_.get(); }
|
|
||||||
|
|
||||||
void SetOwnerWindow(BaseWindow* window);
|
|
||||||
|
|
||||||
int32_t ID() const { return id_; }
|
|
||||||
|
|
||||||
int NonClientHitTest(const gfx::Point& point) override;
|
|
||||||
|
|
||||||
// disable copy
|
|
||||||
BrowserView(const BrowserView&) = delete;
|
|
||||||
BrowserView& operator=(const BrowserView&) = delete;
|
|
||||||
|
|
||||||
gfx::Rect GetBounds();
|
|
||||||
void SetBounds(const gfx::Rect& bounds);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BrowserView(gin::Arguments* args, const gin_helper::Dictionary& options);
|
|
||||||
~BrowserView() override;
|
|
||||||
|
|
||||||
// content::WebContentsObserver:
|
|
||||||
void WebContentsDestroyed() override;
|
|
||||||
|
|
||||||
// ExtendedWebContentsObserver:
|
|
||||||
void OnCloseContents() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void SetAutoResize(AutoResizeFlags flags);
|
|
||||||
void SetBackgroundColor(const std::string& color_name);
|
|
||||||
v8::Local<v8::Value> GetWebContents(v8::Isolate*);
|
|
||||||
|
|
||||||
v8::Global<v8::Value> web_contents_;
|
|
||||||
class raw_ptr<WebContents> api_web_contents_ = nullptr;
|
|
||||||
|
|
||||||
std::unique_ptr<NativeBrowserView> view_;
|
|
||||||
base::WeakPtr<BaseWindow> owner_window_;
|
|
||||||
|
|
||||||
int32_t id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace electron::api
|
|
||||||
|
|
||||||
#endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "content/public/common/color_parser.h"
|
#include "content/public/common/color_parser.h"
|
||||||
#include "shell/browser/api/electron_api_web_contents_view.h"
|
#include "shell/browser/api/electron_api_web_contents_view.h"
|
||||||
#include "shell/browser/browser.h"
|
#include "shell/browser/browser.h"
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
#include "shell/browser/web_contents_preferences.h"
|
#include "shell/browser/web_contents_preferences.h"
|
||||||
#include "shell/browser/window_list.h"
|
#include "shell/browser/window_list.h"
|
||||||
#include "shell/common/color_util.h"
|
#include "shell/common/color_util.h"
|
||||||
|
@ -70,6 +69,9 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||||
web_preferences.SetHidden("webContents", value);
|
web_preferences.SetHidden("webContents", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!web_preferences.Has(options::kShow))
|
||||||
|
web_preferences.Set(options::kShow, true);
|
||||||
|
|
||||||
// Creates the WebContentsView.
|
// Creates the WebContentsView.
|
||||||
gin::Handle<WebContentsView> web_contents_view =
|
gin::Handle<WebContentsView> web_contents_view =
|
||||||
WebContentsView::Create(isolate, web_preferences);
|
WebContentsView::Create(isolate, web_preferences);
|
||||||
|
@ -101,8 +103,6 @@ BrowserWindow::~BrowserWindow() {
|
||||||
// Cleanup the observers if user destroyed this instance directly instead of
|
// Cleanup the observers if user destroyed this instance directly instead of
|
||||||
// gracefully closing content::WebContents.
|
// gracefully closing content::WebContents.
|
||||||
api_web_contents_->RemoveObserver(this);
|
api_web_contents_->RemoveObserver(this);
|
||||||
// Destroy the WebContents.
|
|
||||||
OnCloseContents();
|
|
||||||
api_web_contents_->Destroy();
|
api_web_contents_->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,6 @@ void BrowserWindow::WebContentsDestroyed() {
|
||||||
CloseImmediately();
|
CloseImmediately();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnCloseContents() {
|
|
||||||
BaseWindow::ResetBrowserViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
||||||
window_unresponsive_closure_.Cancel();
|
window_unresponsive_closure_.Cancel();
|
||||||
Emit("responsive");
|
Emit("responsive");
|
||||||
|
@ -187,26 +183,6 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
|
||||||
// Required to make beforeunload handler work.
|
// Required to make beforeunload handler work.
|
||||||
api_web_contents_->NotifyUserActivation();
|
api_web_contents_->NotifyUserActivation();
|
||||||
|
|
||||||
// Trigger beforeunload events for associated BrowserViews.
|
|
||||||
for (NativeBrowserView* view : window_->browser_views()) {
|
|
||||||
auto* iwc = view->GetInspectableWebContents();
|
|
||||||
if (!iwc)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto* vwc = iwc->GetWebContents();
|
|
||||||
auto* api_web_contents = api::WebContents::From(vwc);
|
|
||||||
|
|
||||||
// Required to make beforeunload handler work.
|
|
||||||
if (api_web_contents)
|
|
||||||
api_web_contents->NotifyUserActivation();
|
|
||||||
|
|
||||||
if (vwc) {
|
|
||||||
if (vwc->NeedToFireBeforeUnloadOrUnloadEvents()) {
|
|
||||||
vwc->DispatchBeforeUnload(false /* auto_cancel */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (web_contents()->NeedToFireBeforeUnloadOrUnloadEvents()) {
|
if (web_contents()->NeedToFireBeforeUnloadOrUnloadEvents()) {
|
||||||
web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
|
web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
|
||||||
} else {
|
} else {
|
||||||
|
@ -288,16 +264,10 @@ void BrowserWindow::Blur() {
|
||||||
void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
|
void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
|
||||||
BaseWindow::SetBackgroundColor(color_name);
|
BaseWindow::SetBackgroundColor(color_name);
|
||||||
SkColor color = ParseCSSColor(color_name);
|
SkColor color = ParseCSSColor(color_name);
|
||||||
web_contents()->SetPageBaseBackgroundColor(color);
|
|
||||||
auto* rwhv = web_contents()->GetRenderWidgetHostView();
|
|
||||||
if (rwhv) {
|
|
||||||
rwhv->SetBackgroundColor(color);
|
|
||||||
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
|
|
||||||
->SetContentBackgroundColor(color);
|
|
||||||
}
|
|
||||||
// Also update the web preferences object otherwise the view will be reset on
|
|
||||||
// the next load URL call
|
|
||||||
if (api_web_contents_) {
|
if (api_web_contents_) {
|
||||||
|
api_web_contents_->SetBackgroundColor(color);
|
||||||
|
// Also update the web preferences object otherwise the view will be reset
|
||||||
|
// on the next load URL call
|
||||||
auto* web_preferences =
|
auto* web_preferences =
|
||||||
WebContentsPreferences::From(api_web_contents_->web_contents());
|
WebContentsPreferences::From(api_web_contents_->web_contents());
|
||||||
if (web_preferences) {
|
if (web_preferences) {
|
||||||
|
@ -306,13 +276,6 @@ void BrowserWindow::SetBackgroundColor(const std::string& color_name) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::SetBrowserView(
|
|
||||||
absl::optional<gin::Handle<BrowserView>> browser_view) {
|
|
||||||
BaseWindow::ResetBrowserViews();
|
|
||||||
if (browser_view)
|
|
||||||
BaseWindow::AddBrowserView(*browser_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::FocusOnWebView() {
|
void BrowserWindow::FocusOnWebView() {
|
||||||
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
|
web_contents()->GetRenderViewHost()->GetWidget()->Focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ class BrowserWindow : public BaseWindow,
|
||||||
void WebContentsDestroyed() override;
|
void WebContentsDestroyed() override;
|
||||||
|
|
||||||
// ExtendedWebContentsObserver:
|
// ExtendedWebContentsObserver:
|
||||||
void OnCloseContents() override;
|
|
||||||
void OnSetContentBounds(const gfx::Rect& rect) override;
|
void OnSetContentBounds(const gfx::Rect& rect) override;
|
||||||
void OnActivateContents() override;
|
void OnActivateContents() override;
|
||||||
void OnPageTitleUpdated(const std::u16string& title,
|
void OnPageTitleUpdated(const std::u16string& title,
|
||||||
|
@ -69,8 +68,6 @@ class BrowserWindow : public BaseWindow,
|
||||||
void Focus() override;
|
void Focus() override;
|
||||||
void Blur() override;
|
void Blur() override;
|
||||||
void SetBackgroundColor(const std::string& color_name) override;
|
void SetBackgroundColor(const std::string& color_name) override;
|
||||||
void SetBrowserView(
|
|
||||||
absl::optional<gin::Handle<BrowserView>> browser_view) override;
|
|
||||||
void OnWindowShow() override;
|
void OnWindowShow() override;
|
||||||
void OnWindowHide() override;
|
void OnWindowHide() override;
|
||||||
|
|
||||||
|
|
|
@ -4,34 +4,357 @@
|
||||||
|
|
||||||
#include "shell/browser/api/electron_api_view.h"
|
#include "shell/browser/api/electron_api_view.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "gin/data_object_builder.h"
|
||||||
|
#include "gin/wrappable.h"
|
||||||
|
#include "shell/browser/javascript_environment.h"
|
||||||
|
#include "shell/common/gin_converters/callback_converter.h"
|
||||||
|
#include "shell/common/gin_converters/gfx_converter.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
|
#include "ui/views/background.h"
|
||||||
|
#include "ui/views/layout/flex_layout.h"
|
||||||
|
#include "ui/views/layout/layout_manager_base.h"
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
#include "shell/browser/animation_util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gin {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::ChildLayout> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
views::ChildLayout* out) {
|
||||||
|
gin_helper::Dictionary dict;
|
||||||
|
if (!gin::ConvertFromV8(isolate, val, &dict))
|
||||||
|
return false;
|
||||||
|
gin::Handle<electron::api::View> view;
|
||||||
|
if (!dict.Get("view", &view))
|
||||||
|
return false;
|
||||||
|
out->child_view = view->view();
|
||||||
|
if (dict.Has("bounds"))
|
||||||
|
dict.Get("bounds", &out->bounds);
|
||||||
|
out->visible = true;
|
||||||
|
if (dict.Has("visible"))
|
||||||
|
dict.Get("visible", &out->visible);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::ProposedLayout> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
views::ProposedLayout* out) {
|
||||||
|
gin_helper::Dictionary dict;
|
||||||
|
if (!gin::ConvertFromV8(isolate, val, &dict))
|
||||||
|
return false;
|
||||||
|
if (!dict.Get("size", &out->host_size))
|
||||||
|
return false;
|
||||||
|
if (!dict.Get("layouts", &out->child_layouts))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::LayoutOrientation> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
views::LayoutOrientation* out) {
|
||||||
|
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
|
||||||
|
if (orientation == "horizontal") {
|
||||||
|
*out = views::LayoutOrientation::kHorizontal;
|
||||||
|
} else if (orientation == "vertical") {
|
||||||
|
*out = views::LayoutOrientation::kVertical;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::LayoutAlignment> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
views::LayoutAlignment* out) {
|
||||||
|
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
|
||||||
|
if (orientation == "start") {
|
||||||
|
*out = views::LayoutAlignment::kStart;
|
||||||
|
} else if (orientation == "center") {
|
||||||
|
*out = views::LayoutAlignment::kCenter;
|
||||||
|
} else if (orientation == "end") {
|
||||||
|
*out = views::LayoutAlignment::kEnd;
|
||||||
|
} else if (orientation == "stretch") {
|
||||||
|
*out = views::LayoutAlignment::kStretch;
|
||||||
|
} else if (orientation == "baseline") {
|
||||||
|
*out = views::LayoutAlignment::kBaseline;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::FlexAllocationOrder> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
views::FlexAllocationOrder* out) {
|
||||||
|
std::string orientation = base::ToLowerASCII(gin::V8ToString(isolate, val));
|
||||||
|
if (orientation == "normal") {
|
||||||
|
*out = views::FlexAllocationOrder::kNormal;
|
||||||
|
} else if (orientation == "reverse") {
|
||||||
|
*out = views::FlexAllocationOrder::kReverse;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::SizeBound> {
|
||||||
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
const views::SizeBound& in) {
|
||||||
|
if (in.is_bounded())
|
||||||
|
return v8::Integer::New(isolate, in.value());
|
||||||
|
return v8::Number::New(isolate, std::numeric_limits<double>::infinity());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<views::SizeBounds> {
|
||||||
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
const views::SizeBounds& in) {
|
||||||
|
return gin::DataObjectBuilder(isolate)
|
||||||
|
.Set("width", in.width())
|
||||||
|
.Set("height", in.height())
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace gin
|
||||||
|
|
||||||
namespace electron::api {
|
namespace electron::api {
|
||||||
|
|
||||||
|
using LayoutCallback = base::RepeatingCallback<views::ProposedLayout(
|
||||||
|
const views::SizeBounds& size_bounds)>;
|
||||||
|
|
||||||
|
class JSLayoutManager : public views::LayoutManagerBase {
|
||||||
|
public:
|
||||||
|
explicit JSLayoutManager(LayoutCallback layout_callback)
|
||||||
|
: layout_callback_(std::move(layout_callback)) {}
|
||||||
|
~JSLayoutManager() override {}
|
||||||
|
|
||||||
|
views::ProposedLayout CalculateProposedLayout(
|
||||||
|
const views::SizeBounds& size_bounds) const override {
|
||||||
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
return layout_callback_.Run(size_bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LayoutCallback layout_callback_;
|
||||||
|
};
|
||||||
|
|
||||||
View::View(views::View* view) : view_(view) {
|
View::View(views::View* view) : view_(view) {
|
||||||
view_->set_owned_by_client();
|
view_->set_owned_by_client();
|
||||||
|
view_->AddObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
View::View() : View(new views::View()) {}
|
View::View() : View(new views::View()) {}
|
||||||
|
|
||||||
View::~View() {
|
View::~View() {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
view_->RemoveObserver(this);
|
||||||
if (delete_view_)
|
if (delete_view_)
|
||||||
delete view_;
|
delete view_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void View::AddChildViewAt(gin::Handle<View> child,
|
||||||
|
absl::optional<size_t> maybe_index) {
|
||||||
|
// TODO(nornagon): !view_ is only for supporting the weird case of
|
||||||
|
// WebContentsView's view being deleted when the underlying WebContents is
|
||||||
|
// destroyed (on non-Mac). We should fix that so that WebContentsView always
|
||||||
|
// has a View, possibly a wrapper view around the underlying platform View.
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
size_t index =
|
||||||
|
std::min(child_views_.size(), maybe_index.value_or(child_views_.size()));
|
||||||
|
child_views_.emplace(child_views_.begin() + index, // index
|
||||||
|
isolate(), child->GetWrapper()); // v8::Global(args...)
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
// Disable the implicit CALayer animations that happen by default when adding
|
||||||
|
// or removing sublayers.
|
||||||
|
// See
|
||||||
|
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/ReactingtoLayerChanges/ReactingtoLayerChanges.html
|
||||||
|
// and https://github.com/electron/electron/pull/14911
|
||||||
|
// TODO(nornagon): Disabling these CALayer animations (which are specific to
|
||||||
|
// WebContentsView, I think) seems like this is something that remote_cocoa
|
||||||
|
// or views should be taking care of, but isn't. This should be pushed
|
||||||
|
// upstream.
|
||||||
|
ScopedCAActionDisabler disable_animations;
|
||||||
|
#endif
|
||||||
|
view_->AddChildViewAt(child->view(), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::RemoveChildView(gin::Handle<View> child) {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
if (!child->view())
|
||||||
|
return;
|
||||||
|
auto it = std::find(child_views_.begin(), child_views_.end(), child.ToV8());
|
||||||
|
if (it != child_views_.end()) {
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
ScopedCAActionDisabler disable_animations;
|
||||||
|
#endif
|
||||||
|
view_->RemoveChildView(child->view());
|
||||||
|
child_views_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::SetBounds(const gfx::Rect& bounds) {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
view_->SetBoundsRect(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx::Rect View::GetBounds() {
|
||||||
|
if (!view_)
|
||||||
|
return gfx::Rect();
|
||||||
|
return view_->bounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::SetLayout(v8::Isolate* isolate, v8::Local<v8::Object> value) {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
gin_helper::Dictionary dict(isolate, value);
|
||||||
|
LayoutCallback calculate_proposed_layout;
|
||||||
|
if (dict.Get("calculateProposedLayout", &calculate_proposed_layout)) {
|
||||||
|
view_->SetLayoutManager(std::make_unique<JSLayoutManager>(
|
||||||
|
std::move(calculate_proposed_layout)));
|
||||||
|
} else {
|
||||||
|
auto* layout =
|
||||||
|
view_->SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||||
|
views::LayoutOrientation orientation;
|
||||||
|
if (dict.Get("orientation", &orientation))
|
||||||
|
layout->SetOrientation(orientation);
|
||||||
|
views::LayoutAlignment main_axis_alignment;
|
||||||
|
if (dict.Get("mainAxisAlignment", &main_axis_alignment))
|
||||||
|
layout->SetMainAxisAlignment(main_axis_alignment);
|
||||||
|
views::LayoutAlignment cross_axis_alignment;
|
||||||
|
if (dict.Get("crossAxisAlignment", &cross_axis_alignment))
|
||||||
|
layout->SetCrossAxisAlignment(cross_axis_alignment);
|
||||||
|
gfx::Insets interior_margin;
|
||||||
|
if (dict.Get("interiorMargin", &interior_margin))
|
||||||
|
layout->SetInteriorMargin(interior_margin);
|
||||||
|
int minimum_cross_axis_size;
|
||||||
|
if (dict.Get("minimumCrossAxisSize", &minimum_cross_axis_size))
|
||||||
|
layout->SetMinimumCrossAxisSize(minimum_cross_axis_size);
|
||||||
|
bool collapse_margins;
|
||||||
|
if (dict.Has("collapseMargins") &&
|
||||||
|
dict.Get("collapseMargins", &collapse_margins))
|
||||||
|
layout->SetCollapseMargins(collapse_margins);
|
||||||
|
bool include_host_insets_in_layout;
|
||||||
|
if (dict.Has("includeHostInsetsInLayout") &&
|
||||||
|
dict.Get("includeHostInsetsInLayout", &include_host_insets_in_layout))
|
||||||
|
layout->SetIncludeHostInsetsInLayout(include_host_insets_in_layout);
|
||||||
|
bool ignore_default_main_axis_margins;
|
||||||
|
if (dict.Has("ignoreDefaultMainAxisMargins") &&
|
||||||
|
dict.Get("ignoreDefaultMainAxisMargins",
|
||||||
|
&ignore_default_main_axis_margins))
|
||||||
|
layout->SetIgnoreDefaultMainAxisMargins(ignore_default_main_axis_margins);
|
||||||
|
views::FlexAllocationOrder flex_allocation_order;
|
||||||
|
if (dict.Get("flexAllocationOrder", &flex_allocation_order))
|
||||||
|
layout->SetFlexAllocationOrder(flex_allocation_order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<v8::Local<v8::Value>> View::GetChildren() {
|
||||||
|
std::vector<v8::Local<v8::Value>> ret;
|
||||||
|
ret.reserve(child_views_.size());
|
||||||
|
|
||||||
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
|
||||||
|
for (auto& child_view : child_views_)
|
||||||
|
ret.push_back(child_view.Get(isolate));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::SetBackgroundColor(absl::optional<WrappedSkColor> color) {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
view_->SetBackground(color ? views::CreateSolidBackground(*color) : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::SetVisible(bool visible) {
|
||||||
|
if (!view_)
|
||||||
|
return;
|
||||||
|
view_->SetVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::OnViewBoundsChanged(views::View* observed_view) {
|
||||||
|
Emit("bounds-changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::OnViewIsDeleting(views::View* observed_view) {
|
||||||
|
DCHECK_EQ(observed_view, view_);
|
||||||
|
view_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin_helper::WrappableBase* View::New(gin::Arguments* args) {
|
gin_helper::WrappableBase* View::New(gin::Arguments* args) {
|
||||||
auto* view = new View();
|
View* view = new View();
|
||||||
view->InitWithArgs(args);
|
view->InitWithArgs(args);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
v8::Local<v8::Function> View::GetConstructor(v8::Isolate* isolate) {
|
||||||
|
static base::NoDestructor<v8::Global<v8::Function>> constructor;
|
||||||
|
if (constructor.get()->IsEmpty()) {
|
||||||
|
constructor->Reset(isolate, gin_helper::CreateConstructor<View>(
|
||||||
|
isolate, base::BindRepeating(&View::New)));
|
||||||
|
}
|
||||||
|
return v8::Local<v8::Function>::New(isolate, *constructor.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
gin::Handle<View> View::Create(v8::Isolate* isolate) {
|
||||||
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||||
|
v8::Local<v8::Object> obj;
|
||||||
|
if (GetConstructor(isolate)->NewInstance(context, 0, nullptr).ToLocal(&obj)) {
|
||||||
|
gin::Handle<View> view;
|
||||||
|
if (gin::ConvertFromV8(isolate, obj, &view))
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
return gin::Handle<View>();
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void View::BuildPrototype(v8::Isolate* isolate,
|
void View::BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::FunctionTemplate> prototype) {
|
v8::Local<v8::FunctionTemplate> prototype) {
|
||||||
prototype->SetClassName(gin::StringToV8(isolate, "View"));
|
prototype->SetClassName(gin::StringToV8(isolate, "View"));
|
||||||
|
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||||
|
.SetMethod("addChildView", &View::AddChildViewAt)
|
||||||
|
.SetMethod("removeChildView", &View::RemoveChildView)
|
||||||
|
.SetProperty("children", &View::GetChildren)
|
||||||
|
.SetMethod("setBounds", &View::SetBounds)
|
||||||
|
.SetMethod("getBounds", &View::GetBounds)
|
||||||
|
.SetMethod("setBackgroundColor", &View::SetBackgroundColor)
|
||||||
|
.SetMethod("setLayout", &View::SetLayout)
|
||||||
|
.SetMethod("setVisible", &View::SetVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace electron::api
|
} // namespace electron::api
|
||||||
|
@ -45,14 +368,9 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
void* priv) {
|
void* priv) {
|
||||||
v8::Isolate* isolate = context->GetIsolate();
|
v8::Isolate* isolate = context->GetIsolate();
|
||||||
View::SetConstructor(isolate, base::BindRepeating(&View::New));
|
|
||||||
|
|
||||||
gin_helper::Dictionary constructor(
|
|
||||||
isolate,
|
|
||||||
View::GetConstructor(isolate)->GetFunction(context).ToLocalChecked());
|
|
||||||
|
|
||||||
gin_helper::Dictionary dict(isolate, exports);
|
gin_helper::Dictionary dict(isolate, exports);
|
||||||
dict.Set("View", constructor);
|
dict.Set("View", View::GetConstructor(isolate));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -7,18 +7,39 @@
|
||||||
|
|
||||||
#include "base/memory/raw_ptr.h"
|
#include "base/memory/raw_ptr.h"
|
||||||
#include "gin/handle.h"
|
#include "gin/handle.h"
|
||||||
#include "shell/common/gin_helper/wrappable.h"
|
#include "shell/common/color_util.h"
|
||||||
|
#include "shell/common/gin_helper/event_emitter.h"
|
||||||
#include "ui/views/view.h"
|
#include "ui/views/view.h"
|
||||||
|
#include "ui/views/view_observer.h"
|
||||||
|
#include "v8/include/v8-value.h"
|
||||||
|
|
||||||
namespace electron::api {
|
namespace electron::api {
|
||||||
|
|
||||||
class View : public gin_helper::Wrappable<View> {
|
class View : public gin_helper::EventEmitter<View>, public views::ViewObserver {
|
||||||
public:
|
public:
|
||||||
static gin_helper::WrappableBase* New(gin::Arguments* args);
|
static gin_helper::WrappableBase* New(gin::Arguments* args);
|
||||||
|
static gin::Handle<View> Create(v8::Isolate* isolate);
|
||||||
|
|
||||||
|
// Return the cached constructor function.
|
||||||
|
static v8::Local<v8::Function> GetConstructor(v8::Isolate* isolate);
|
||||||
|
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
static void BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::FunctionTemplate> prototype);
|
v8::Local<v8::FunctionTemplate> prototype);
|
||||||
|
|
||||||
|
void AddChildViewAt(gin::Handle<View> child, absl::optional<size_t> index);
|
||||||
|
void RemoveChildView(gin::Handle<View> child);
|
||||||
|
|
||||||
|
void SetBounds(const gfx::Rect& bounds);
|
||||||
|
gfx::Rect GetBounds();
|
||||||
|
void SetLayout(v8::Isolate* isolate, v8::Local<v8::Object> value);
|
||||||
|
std::vector<v8::Local<v8::Value>> GetChildren();
|
||||||
|
void SetBackgroundColor(absl::optional<WrappedSkColor> color);
|
||||||
|
void SetVisible(bool visible);
|
||||||
|
|
||||||
|
// views::ViewObserver
|
||||||
|
void OnViewBoundsChanged(views::View* observed_view) override;
|
||||||
|
void OnViewIsDeleting(views::View* observed_view) override;
|
||||||
|
|
||||||
views::View* view() const { return view_; }
|
views::View* view() const { return view_; }
|
||||||
|
|
||||||
// disable copy
|
// disable copy
|
||||||
|
@ -34,6 +55,8 @@ class View : public gin_helper::Wrappable<View> {
|
||||||
void set_delete_view(bool should) { delete_view_ = should; }
|
void set_delete_view(bool should) { delete_view_ = should; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<v8::Global<v8::Object>> child_views_;
|
||||||
|
|
||||||
bool delete_view_ = true;
|
bool delete_view_ = true;
|
||||||
raw_ptr<views::View> view_ = nullptr;
|
raw_ptr<views::View> view_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -717,12 +717,6 @@ bool IsDevToolsFileSystemAdded(content::WebContents* web_contents,
|
||||||
file_system_path);
|
file_system_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetBackgroundColor(content::RenderWidgetHostView* rwhv, SkColor color) {
|
|
||||||
rwhv->SetBackgroundColor(color);
|
|
||||||
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
|
|
||||||
->SetContentBackgroundColor(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
content::RenderFrameHost* GetRenderFrameHost(
|
content::RenderFrameHost* GetRenderFrameHost(
|
||||||
content::NavigationHandle* navigation_handle) {
|
content::NavigationHandle* navigation_handle) {
|
||||||
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
|
int frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
|
||||||
|
@ -739,7 +733,6 @@ content::RenderFrameHost* GetRenderFrameHost(
|
||||||
|
|
||||||
return frame_host;
|
return frame_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
@ -839,10 +832,7 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||||
// Whether to enable DevTools.
|
// Whether to enable DevTools.
|
||||||
options.Get("devTools", &enable_devtools_);
|
options.Get("devTools", &enable_devtools_);
|
||||||
|
|
||||||
// BrowserViews are not attached to a window initially so they should start
|
bool initially_shown = true;
|
||||||
// off as hidden. This is also important for compositor recycling. See:
|
|
||||||
// https://github.com/electron/electron/pull/21372
|
|
||||||
bool initially_shown = type_ != Type::kBrowserView;
|
|
||||||
options.Get(options::kShow, &initially_shown);
|
options.Get(options::kShow, &initially_shown);
|
||||||
|
|
||||||
// Obtain the session.
|
// Obtain the session.
|
||||||
|
@ -1287,13 +1277,11 @@ content::WebContents* WebContents::OpenURLFromTab(
|
||||||
void WebContents::BeforeUnloadFired(content::WebContents* tab,
|
void WebContents::BeforeUnloadFired(content::WebContents* tab,
|
||||||
bool proceed,
|
bool proceed,
|
||||||
bool* proceed_to_fire_unload) {
|
bool* proceed_to_fire_unload) {
|
||||||
if (type_ == Type::kBrowserWindow || type_ == Type::kOffScreen ||
|
|
||||||
type_ == Type::kBrowserView)
|
|
||||||
*proceed_to_fire_unload = proceed;
|
|
||||||
else
|
|
||||||
*proceed_to_fire_unload = true;
|
|
||||||
// Note that Chromium does not emit this for navigations.
|
// Note that Chromium does not emit this for navigations.
|
||||||
Emit("before-unload-fired", proceed);
|
|
||||||
|
// Emit returns true if preventDefault() was called, so !Emit will be true if
|
||||||
|
// the event should proceed.
|
||||||
|
*proceed_to_fire_unload = !Emit("-before-unload-fired", proceed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::SetContentsBounds(content::WebContents* source,
|
void WebContents::SetContentsBounds(content::WebContents* source,
|
||||||
|
@ -1312,12 +1300,7 @@ void WebContents::CloseContents(content::WebContents* source) {
|
||||||
autofill_driver_factory->CloseAllPopups();
|
autofill_driver_factory->CloseAllPopups();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ExtendedWebContentsObserver& observer : observers_)
|
Destroy();
|
||||||
observer.OnCloseContents();
|
|
||||||
|
|
||||||
// This is handled by the embedder frame.
|
|
||||||
if (!IsGuest())
|
|
||||||
Destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::ActivateContents(content::WebContents* source) {
|
void WebContents::ActivateContents(content::WebContents* source) {
|
||||||
|
@ -1599,9 +1582,53 @@ void WebContents::RequestMediaAccessPermission(
|
||||||
permission_helper->RequestMediaAccessPermission(request, std::move(callback));
|
permission_helper->RequestMediaAccessPermission(request, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const void* const kJavaScriptDialogManagerKey = &kJavaScriptDialogManagerKey;
|
||||||
|
|
||||||
content::JavaScriptDialogManager* WebContents::GetJavaScriptDialogManager(
|
content::JavaScriptDialogManager* WebContents::GetJavaScriptDialogManager(
|
||||||
content::WebContents* source) {
|
content::WebContents* source) {
|
||||||
return this;
|
// Indirect these delegate methods through a helper object whose lifetime is
|
||||||
|
// bound to that of the content::WebContents. This prevents the
|
||||||
|
// content::WebContents from calling methods on the Electron WebContents in
|
||||||
|
// the event that the Electron one is destroyed before the content one, as
|
||||||
|
// happens sometimes during shutdown or when webviews are involved.
|
||||||
|
class JSDialogManagerHelper : public content::JavaScriptDialogManager,
|
||||||
|
public base::SupportsUserData::Data {
|
||||||
|
public:
|
||||||
|
void RunJavaScriptDialog(content::WebContents* web_contents,
|
||||||
|
content::RenderFrameHost* rfh,
|
||||||
|
content::JavaScriptDialogType dialog_type,
|
||||||
|
const std::u16string& message_text,
|
||||||
|
const std::u16string& default_prompt_text,
|
||||||
|
DialogClosedCallback callback,
|
||||||
|
bool* did_suppress_message) override {
|
||||||
|
auto* wc = WebContents::From(web_contents);
|
||||||
|
if (wc)
|
||||||
|
wc->RunJavaScriptDialog(web_contents, rfh, dialog_type, message_text,
|
||||||
|
default_prompt_text, std::move(callback),
|
||||||
|
did_suppress_message);
|
||||||
|
}
|
||||||
|
void RunBeforeUnloadDialog(content::WebContents* web_contents,
|
||||||
|
content::RenderFrameHost* rfh,
|
||||||
|
bool is_reload,
|
||||||
|
DialogClosedCallback callback) override {
|
||||||
|
auto* wc = WebContents::From(web_contents);
|
||||||
|
if (wc)
|
||||||
|
wc->RunBeforeUnloadDialog(web_contents, rfh, is_reload,
|
||||||
|
std::move(callback));
|
||||||
|
}
|
||||||
|
void CancelDialogs(content::WebContents* web_contents,
|
||||||
|
bool reset_state) override {
|
||||||
|
auto* wc = WebContents::From(web_contents);
|
||||||
|
if (wc)
|
||||||
|
wc->CancelDialogs(web_contents, reset_state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!source->GetUserData(kJavaScriptDialogManagerKey))
|
||||||
|
source->SetUserData(kJavaScriptDialogManagerKey,
|
||||||
|
std::make_unique<JSDialogManagerHelper>());
|
||||||
|
|
||||||
|
return static_cast<JSDialogManagerHelper*>(
|
||||||
|
source->GetUserData(kJavaScriptDialogManagerKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::OnAudioStateChanged(bool audible) {
|
void WebContents::OnAudioStateChanged(bool audible) {
|
||||||
|
@ -1628,17 +1655,8 @@ void WebContents::HandleNewRenderFrame(
|
||||||
|
|
||||||
// Set the background color of RenderWidgetHostView.
|
// Set the background color of RenderWidgetHostView.
|
||||||
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
||||||
if (web_preferences) {
|
if (web_preferences)
|
||||||
auto maybe_color = web_preferences->GetBackgroundColor();
|
SetBackgroundColor(web_preferences->GetBackgroundColor());
|
||||||
bool guest = IsGuest() || type_ == Type::kBrowserView;
|
|
||||||
|
|
||||||
// If webPreferences has no color stored we need to explicitly set guest
|
|
||||||
// webContents background color to transparent.
|
|
||||||
auto bg_color =
|
|
||||||
maybe_color.value_or(guest ? SK_ColorTRANSPARENT : SK_ColorWHITE);
|
|
||||||
web_contents()->SetPageBaseBackgroundColor(bg_color);
|
|
||||||
SetBackgroundColor(rwhv, bg_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!background_throttling_)
|
if (!background_throttling_)
|
||||||
render_frame_host->GetRenderViewHost()->SetSchedulerThrottling(false);
|
render_frame_host->GetRenderViewHost()->SetSchedulerThrottling(false);
|
||||||
|
@ -2245,6 +2263,11 @@ void WebContents::SetOwnerWindow(NativeWindow* owner_window) {
|
||||||
SetOwnerWindow(GetWebContents(), owner_window);
|
SetOwnerWindow(GetWebContents(), owner_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::SetOwnerBaseWindow(absl::optional<BaseWindow*> owner_window) {
|
||||||
|
SetOwnerWindow(GetWebContents(),
|
||||||
|
owner_window ? (*owner_window)->window() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void WebContents::SetOwnerWindow(content::WebContents* web_contents,
|
void WebContents::SetOwnerWindow(content::WebContents* web_contents,
|
||||||
NativeWindow* owner_window) {
|
NativeWindow* owner_window) {
|
||||||
if (owner_window_) {
|
if (owner_window_) {
|
||||||
|
@ -3754,6 +3777,22 @@ void WebContents::SetImageAnimationPolicy(const std::string& new_policy) {
|
||||||
web_contents()->OnWebPreferencesChanged();
|
web_contents()->OnWebPreferencesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::SetBackgroundColor(absl::optional<SkColor> maybe_color) {
|
||||||
|
web_contents()->SetPageBaseBackgroundColor(maybe_color);
|
||||||
|
|
||||||
|
content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
|
||||||
|
if (!rfh)
|
||||||
|
return;
|
||||||
|
content::RenderWidgetHostView* rwhv = rfh->GetView();
|
||||||
|
if (rwhv) {
|
||||||
|
SkColor color =
|
||||||
|
maybe_color.value_or(IsGuest() ? SK_ColorTRANSPARENT : SK_ColorWHITE);
|
||||||
|
rwhv->SetBackgroundColor(color);
|
||||||
|
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
|
||||||
|
->SetContentBackgroundColor(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WebContents::OnInputEvent(const blink::WebInputEvent& event) {
|
void WebContents::OnInputEvent(const blink::WebInputEvent& event) {
|
||||||
Emit("input-event", event);
|
Emit("input-event", event);
|
||||||
}
|
}
|
||||||
|
@ -4414,6 +4453,7 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
|
||||||
.SetProperty("debugger", &WebContents::Debugger)
|
.SetProperty("debugger", &WebContents::Debugger)
|
||||||
.SetProperty("mainFrame", &WebContents::MainFrame)
|
.SetProperty("mainFrame", &WebContents::MainFrame)
|
||||||
.SetProperty("opener", &WebContents::Opener)
|
.SetProperty("opener", &WebContents::Opener)
|
||||||
|
.SetMethod("_setOwnerWindow", &WebContents::SetOwnerBaseWindow)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4497,14 +4537,8 @@ gin::Handle<WebContents> WebContents::CreateFromWebPreferences(
|
||||||
if (gin::ConvertFromV8(isolate, web_preferences.GetHandle(),
|
if (gin::ConvertFromV8(isolate, web_preferences.GetHandle(),
|
||||||
&web_preferences_dict)) {
|
&web_preferences_dict)) {
|
||||||
existing_preferences->SetFromDictionary(web_preferences_dict);
|
existing_preferences->SetFromDictionary(web_preferences_dict);
|
||||||
absl::optional<SkColor> color =
|
web_contents->SetBackgroundColor(
|
||||||
existing_preferences->GetBackgroundColor();
|
existing_preferences->GetBackgroundColor());
|
||||||
web_contents->web_contents()->SetPageBaseBackgroundColor(color);
|
|
||||||
// Because web preferences don't recognize transparency,
|
|
||||||
// only set rwhv background color if a color exists
|
|
||||||
auto* rwhv = web_contents->web_contents()->GetRenderWidgetHostView();
|
|
||||||
if (rwhv && color.has_value())
|
|
||||||
SetBackgroundColor(rwhv, color.value());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create one if not.
|
// Create one if not.
|
||||||
|
|
|
@ -97,6 +97,8 @@ class OffScreenWebContentsView;
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
class BaseWindow;
|
||||||
|
|
||||||
// Wrapper around the content::WebContents.
|
// Wrapper around the content::WebContents.
|
||||||
class WebContents : public ExclusiveAccessContext,
|
class WebContents : public ExclusiveAccessContext,
|
||||||
public gin::Wrappable<WebContents>,
|
public gin::Wrappable<WebContents>,
|
||||||
|
@ -115,7 +117,9 @@ class WebContents : public ExclusiveAccessContext,
|
||||||
enum class Type {
|
enum class Type {
|
||||||
kBackgroundPage, // An extension background page.
|
kBackgroundPage, // An extension background page.
|
||||||
kBrowserWindow, // Used by BrowserWindow.
|
kBrowserWindow, // Used by BrowserWindow.
|
||||||
kBrowserView, // Used by BrowserView.
|
kBrowserView, // Used by the JS implementation of BrowserView for
|
||||||
|
// backwards compatibility. Otherwise identical to
|
||||||
|
// kBrowserWindow.
|
||||||
kRemote, // Thin wrap around an existing WebContents.
|
kRemote, // Thin wrap around an existing WebContents.
|
||||||
kWebView, // Used by <webview>.
|
kWebView, // Used by <webview>.
|
||||||
kOffScreen, // Used for offscreen rendering
|
kOffScreen, // Used for offscreen rendering
|
||||||
|
@ -399,6 +403,7 @@ class WebContents : public ExclusiveAccessContext,
|
||||||
void SetOwnerWindow(NativeWindow* owner_window);
|
void SetOwnerWindow(NativeWindow* owner_window);
|
||||||
void SetOwnerWindow(content::WebContents* web_contents,
|
void SetOwnerWindow(content::WebContents* web_contents,
|
||||||
NativeWindow* owner_window);
|
NativeWindow* owner_window);
|
||||||
|
void SetOwnerBaseWindow(absl::optional<BaseWindow*> owner_window);
|
||||||
|
|
||||||
// Returns the WebContents managed by this delegate.
|
// Returns the WebContents managed by this delegate.
|
||||||
content::WebContents* GetWebContents() const;
|
content::WebContents* GetWebContents() const;
|
||||||
|
@ -469,6 +474,8 @@ class WebContents : public ExclusiveAccessContext,
|
||||||
void CancelDialogs(content::WebContents* web_contents,
|
void CancelDialogs(content::WebContents* web_contents,
|
||||||
bool reset_state) override;
|
bool reset_state) override;
|
||||||
|
|
||||||
|
void SetBackgroundColor(absl::optional<SkColor> color);
|
||||||
|
|
||||||
SkRegion* draggable_region() {
|
SkRegion* draggable_region() {
|
||||||
return force_non_draggable_ ? nullptr : draggable_region_.get();
|
return force_non_draggable_ ? nullptr : draggable_region_.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,24 @@
|
||||||
#include "shell/browser/api/electron_api_web_contents_view.h"
|
#include "shell/browser/api/electron_api_web_contents_view.h"
|
||||||
|
|
||||||
#include "base/no_destructor.h"
|
#include "base/no_destructor.h"
|
||||||
|
#include "gin/data_object_builder.h"
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
#include "shell/browser/api/electron_api_web_contents.h"
|
||||||
#include "shell/browser/browser.h"
|
#include "shell/browser/browser.h"
|
||||||
|
#include "shell/browser/native_window.h"
|
||||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
||||||
#include "shell/browser/web_contents_preferences.h"
|
#include "shell/browser/web_contents_preferences.h"
|
||||||
|
#include "shell/common/gin_converters/gfx_converter.h"
|
||||||
#include "shell/common/gin_converters/value_converter.h"
|
#include "shell/common/gin_converters/value_converter.h"
|
||||||
#include "shell/common/gin_helper/constructor.h"
|
#include "shell/common/gin_helper/constructor.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
|
#include "shell/common/options_switches.h"
|
||||||
#include "third_party/skia/include/core/SkRegion.h"
|
#include "third_party/skia/include/core/SkRegion.h"
|
||||||
#include "ui/base/hit_test.h"
|
#include "ui/base/hit_test.h"
|
||||||
|
#include "ui/views/layout/flex_layout_types.h"
|
||||||
|
#include "ui/views/view_class_properties.h"
|
||||||
|
#include "ui/views/widget/widget.h"
|
||||||
|
|
||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
|
#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
|
||||||
|
@ -40,6 +47,10 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
|
||||||
// managed by InspectableWebContents.
|
// managed by InspectableWebContents.
|
||||||
set_delete_view(false);
|
set_delete_view(false);
|
||||||
#endif
|
#endif
|
||||||
|
view()->SetProperty(
|
||||||
|
views::kFlexBehaviorKey,
|
||||||
|
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
|
||||||
|
views::MaximumFlexSizeRule::kUnbounded));
|
||||||
Observe(web_contents->web_contents());
|
Observe(web_contents->web_contents());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +60,24 @@ WebContentsView::~WebContentsView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
||||||
return gin::CreateHandle(isolate, api_web_contents_.get());
|
if (api_web_contents_)
|
||||||
|
return gin::CreateHandle(isolate, api_web_contents_.get());
|
||||||
|
else
|
||||||
|
return gin::Handle<WebContents>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContentsView::SetBackgroundColor(absl::optional<WrappedSkColor> color) {
|
||||||
|
View::SetBackgroundColor(color);
|
||||||
|
if (api_web_contents_) {
|
||||||
|
api_web_contents_->SetBackgroundColor(color);
|
||||||
|
// Also update the web preferences object otherwise the view will be reset
|
||||||
|
// on the next load URL call
|
||||||
|
auto* web_preferences =
|
||||||
|
WebContentsPreferences::From(api_web_contents_->web_contents());
|
||||||
|
if (web_preferences) {
|
||||||
|
web_preferences->SetBackgroundColor(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
|
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
|
||||||
|
@ -66,16 +94,40 @@ void WebContentsView::WebContentsDestroyed() {
|
||||||
web_contents_.Reset();
|
web_contents_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContentsView::OnViewAddedToWidget(views::View* observed_view) {
|
||||||
|
DCHECK_EQ(observed_view, view());
|
||||||
|
views::Widget* widget = view()->GetWidget();
|
||||||
|
auto* native_window = static_cast<NativeWindow*>(
|
||||||
|
widget->GetNativeWindowProperty(electron::kElectronNativeWindowKey));
|
||||||
|
if (!native_window)
|
||||||
|
return;
|
||||||
|
native_window->AddDraggableRegionProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContentsView::OnViewRemovedFromWidget(views::View* observed_view) {
|
||||||
|
DCHECK_EQ(observed_view, view());
|
||||||
|
views::Widget* widget = view()->GetWidget();
|
||||||
|
auto* native_window = static_cast<NativeWindow*>(
|
||||||
|
widget->GetNativeWindowProperty(kElectronNativeWindowKey));
|
||||||
|
if (!native_window)
|
||||||
|
return;
|
||||||
|
native_window->RemoveDraggableRegionProvider(this);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin::Handle<WebContentsView> WebContentsView::Create(
|
gin::Handle<WebContentsView> WebContentsView::Create(
|
||||||
v8::Isolate* isolate,
|
v8::Isolate* isolate,
|
||||||
const gin_helper::Dictionary& web_preferences) {
|
const gin_helper::Dictionary& web_preferences) {
|
||||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||||
v8::Local<v8::Value> arg = gin::ConvertToV8(isolate, web_preferences);
|
v8::Local<v8::Value> arg = gin::DataObjectBuilder(isolate)
|
||||||
v8::Local<v8::Object> obj;
|
.Set("webPreferences", web_preferences)
|
||||||
if (GetConstructor(isolate)->NewInstance(context, 1, &arg).ToLocal(&obj)) {
|
.Build();
|
||||||
|
v8::Local<v8::Object> web_contents_view_obj;
|
||||||
|
if (GetConstructor(isolate)
|
||||||
|
->NewInstance(context, 1, &arg)
|
||||||
|
.ToLocal(&web_contents_view_obj)) {
|
||||||
gin::Handle<WebContentsView> web_contents_view;
|
gin::Handle<WebContentsView> web_contents_view;
|
||||||
if (gin::ConvertFromV8(isolate, obj, &web_contents_view))
|
if (gin::ConvertFromV8(isolate, web_contents_view_obj, &web_contents_view))
|
||||||
return web_contents_view;
|
return web_contents_view;
|
||||||
}
|
}
|
||||||
return gin::Handle<WebContentsView>();
|
return gin::Handle<WebContentsView>();
|
||||||
|
@ -93,9 +145,30 @@ v8::Local<v8::Function> WebContentsView::GetConstructor(v8::Isolate* isolate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin_helper::WrappableBase* WebContentsView::New(
|
gin_helper::WrappableBase* WebContentsView::New(gin_helper::Arguments* args) {
|
||||||
gin_helper::Arguments* args,
|
gin_helper::Dictionary web_preferences;
|
||||||
const gin_helper::Dictionary& web_preferences) {
|
{
|
||||||
|
v8::Local<v8::Value> options_value;
|
||||||
|
if (args->GetNext(&options_value)) {
|
||||||
|
gin_helper::Dictionary options;
|
||||||
|
if (!gin::ConvertFromV8(args->isolate(), options_value, &options)) {
|
||||||
|
args->ThrowError("options must be an object");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
v8::Local<v8::Value> web_preferences_value;
|
||||||
|
if (options.Get("webPreferences", &web_preferences_value)) {
|
||||||
|
if (!gin::ConvertFromV8(args->isolate(), web_preferences_value,
|
||||||
|
&web_preferences)) {
|
||||||
|
args->ThrowError("options.webPreferences must be an object");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (web_preferences.IsEmpty())
|
||||||
|
web_preferences = gin_helper::Dictionary::CreateEmpty(args->isolate());
|
||||||
|
if (!web_preferences.Has(options::kShow))
|
||||||
|
web_preferences.Set(options::kShow, false);
|
||||||
auto web_contents =
|
auto web_contents =
|
||||||
WebContents::CreateFromWebPreferences(args->isolate(), web_preferences);
|
WebContents::CreateFromWebPreferences(args->isolate(), web_preferences);
|
||||||
|
|
||||||
|
@ -111,6 +184,7 @@ void WebContentsView::BuildPrototype(
|
||||||
v8::Local<v8::FunctionTemplate> prototype) {
|
v8::Local<v8::FunctionTemplate> prototype) {
|
||||||
prototype->SetClassName(gin::StringToV8(isolate, "WebContentsView"));
|
prototype->SetClassName(gin::StringToV8(isolate, "WebContentsView"));
|
||||||
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||||
|
.SetMethod("setBackgroundColor", &WebContentsView::SetBackgroundColor)
|
||||||
.SetProperty("webContents", &WebContentsView::GetWebContents);
|
.SetProperty("webContents", &WebContentsView::GetWebContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class WebContentsView : public View,
|
||||||
|
|
||||||
// Public APIs.
|
// Public APIs.
|
||||||
gin::Handle<WebContents> GetWebContents(v8::Isolate* isolate);
|
gin::Handle<WebContents> GetWebContents(v8::Isolate* isolate);
|
||||||
|
void SetBackgroundColor(absl::optional<WrappedSkColor> color);
|
||||||
|
|
||||||
int NonClientHitTest(const gfx::Point& point) override;
|
int NonClientHitTest(const gfx::Point& point) override;
|
||||||
|
|
||||||
|
@ -47,10 +48,12 @@ class WebContentsView : public View,
|
||||||
// content::WebContentsObserver:
|
// content::WebContentsObserver:
|
||||||
void WebContentsDestroyed() override;
|
void WebContentsDestroyed() override;
|
||||||
|
|
||||||
|
// views::ViewObserver
|
||||||
|
void OnViewAddedToWidget(views::View* view) override;
|
||||||
|
void OnViewRemovedFromWidget(views::View* view) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static gin_helper::WrappableBase* New(
|
static gin_helper::WrappableBase* New(gin_helper::Arguments* args);
|
||||||
gin_helper::Arguments* args,
|
|
||||||
const gin_helper::Dictionary& web_preferences);
|
|
||||||
|
|
||||||
// Keep a reference to v8 wrapper.
|
// Keep a reference to v8 wrapper.
|
||||||
v8::Global<v8::Value> web_contents_;
|
v8::Global<v8::Value> web_contents_;
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "content/public/browser/render_widget_host_view.h"
|
#include "content/public/browser/render_widget_host_view.h"
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
#include "shell/browser/api/electron_api_web_contents.h"
|
||||||
#include "shell/browser/javascript_environment.h"
|
#include "shell/browser/javascript_environment.h"
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
#include "shell/browser/native_window.h"
|
#include "shell/browser/native_window.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
@ -60,21 +59,6 @@ void AutofillDriver::ShowAutofillPopup(
|
||||||
embedder_frame_host = embedder->web_contents()->GetPrimaryMainFrame();
|
embedder_frame_host = embedder->web_contents()->GetPrimaryMainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that if the WebContents belongs to a BrowserView,
|
|
||||||
// the popup is positioned relative to the BrowserView's bounds.
|
|
||||||
for (NativeBrowserView* bv : owner_window->browser_views()) {
|
|
||||||
auto* iwc = bv->GetInspectableWebContents();
|
|
||||||
if (!iwc)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto* awc = api::WebContents::From(iwc->GetWebContents());
|
|
||||||
if (awc == web_contents) {
|
|
||||||
auto bv_origin = bv->GetBounds().origin();
|
|
||||||
popup_bounds.Offset(gfx::Vector2dF(bv_origin.x(), bv_origin.y()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr,
|
autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr,
|
||||||
owner_window->content_view(), popup_bounds);
|
owner_window->content_view(), popup_bounds);
|
||||||
autofill_popup_->SetItems(values, labels);
|
autofill_popup_->SetItems(values, labels);
|
||||||
|
|
|
@ -18,7 +18,6 @@ namespace electron {
|
||||||
// Observer to dispatch those events.
|
// Observer to dispatch those events.
|
||||||
class ExtendedWebContentsObserver : public base::CheckedObserver {
|
class ExtendedWebContentsObserver : public base::CheckedObserver {
|
||||||
public:
|
public:
|
||||||
virtual void OnCloseContents() {}
|
|
||||||
virtual void OnDraggableRegionsUpdated(
|
virtual void OnDraggableRegionsUpdated(
|
||||||
const std::vector<mojom::DraggableRegionPtr>& regions) {}
|
const std::vector<mojom::DraggableRegionPtr>& regions) {}
|
||||||
virtual void OnSetContentBounds(const gfx::Rect& rect) {}
|
virtual void OnSetContentBounds(const gfx::Rect& rect) {}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
|
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
|
||||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
NativeBrowserView::NativeBrowserView(
|
|
||||||
InspectableWebContents* inspectable_web_contents)
|
|
||||||
: inspectable_web_contents_(inspectable_web_contents) {
|
|
||||||
Observe(inspectable_web_contents_->GetWebContents());
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeBrowserView::~NativeBrowserView() = default;
|
|
||||||
|
|
||||||
InspectableWebContentsView* NativeBrowserView::GetInspectableWebContentsView() {
|
|
||||||
if (!inspectable_web_contents_)
|
|
||||||
return nullptr;
|
|
||||||
return inspectable_web_contents_->GetView();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserView::WebContentsDestroyed() {
|
|
||||||
inspectable_web_contents_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace electron
|
|
|
@ -1,61 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_H_
|
|
||||||
#define ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_H_
|
|
||||||
|
|
||||||
#include "base/memory/raw_ptr.h"
|
|
||||||
#include "content/public/browser/web_contents.h"
|
|
||||||
#include "content/public/browser/web_contents_observer.h"
|
|
||||||
#include "third_party/skia/include/core/SkColor.h"
|
|
||||||
|
|
||||||
namespace gfx {
|
|
||||||
class Rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
enum AutoResizeFlags {
|
|
||||||
kAutoResizeWidth = 0x1,
|
|
||||||
kAutoResizeHeight = 0x2,
|
|
||||||
kAutoResizeHorizontal = 0x4,
|
|
||||||
kAutoResizeVertical = 0x8,
|
|
||||||
};
|
|
||||||
|
|
||||||
class InspectableWebContents;
|
|
||||||
class InspectableWebContentsView;
|
|
||||||
|
|
||||||
class NativeBrowserView : public content::WebContentsObserver {
|
|
||||||
public:
|
|
||||||
~NativeBrowserView() override;
|
|
||||||
|
|
||||||
// disable copy
|
|
||||||
NativeBrowserView(const NativeBrowserView&) = delete;
|
|
||||||
NativeBrowserView& operator=(const NativeBrowserView&) = delete;
|
|
||||||
|
|
||||||
static NativeBrowserView* Create(
|
|
||||||
InspectableWebContents* inspectable_web_contents);
|
|
||||||
|
|
||||||
InspectableWebContents* GetInspectableWebContents() {
|
|
||||||
return inspectable_web_contents_;
|
|
||||||
}
|
|
||||||
|
|
||||||
InspectableWebContentsView* GetInspectableWebContentsView();
|
|
||||||
|
|
||||||
virtual void SetAutoResizeFlags(uint8_t flags) = 0;
|
|
||||||
virtual void SetBounds(const gfx::Rect& bounds) = 0;
|
|
||||||
virtual gfx::Rect GetBounds() = 0;
|
|
||||||
virtual void SetBackgroundColor(SkColor color) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit NativeBrowserView(InspectableWebContents* inspectable_web_contents);
|
|
||||||
// content::WebContentsObserver:
|
|
||||||
void WebContentsDestroyed() override;
|
|
||||||
|
|
||||||
raw_ptr<InspectableWebContents> inspectable_web_contents_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace electron
|
|
||||||
|
|
||||||
#endif // ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_H_
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
|
|
||||||
#define ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
class NativeBrowserViewMac : public NativeBrowserView {
|
|
||||||
public:
|
|
||||||
explicit NativeBrowserViewMac(
|
|
||||||
InspectableWebContents* inspectable_web_contents);
|
|
||||||
~NativeBrowserViewMac() override;
|
|
||||||
|
|
||||||
void SetAutoResizeFlags(uint8_t flags) override;
|
|
||||||
void SetBounds(const gfx::Rect& bounds) override;
|
|
||||||
gfx::Rect GetBounds() override;
|
|
||||||
void SetBackgroundColor(SkColor color) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace electron
|
|
||||||
|
|
||||||
#endif // ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_MAC_H_
|
|
|
@ -1,96 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view_mac.h"
|
|
||||||
|
|
||||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
|
||||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
|
||||||
#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 electron {
|
|
||||||
|
|
||||||
NativeBrowserViewMac::NativeBrowserViewMac(
|
|
||||||
InspectableWebContents* inspectable_web_contents)
|
|
||||||
: NativeBrowserView(inspectable_web_contents) {
|
|
||||||
auto* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetNativeView().GetNativeNSView();
|
|
||||||
view.autoresizingMask = kDefaultAutoResizingMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeBrowserViewMac::~NativeBrowserViewMac() = default;
|
|
||||||
|
|
||||||
void NativeBrowserViewMac::SetAutoResizeFlags(uint8_t flags) {
|
|
||||||
NSAutoresizingMaskOptions autoresizing_mask = kDefaultAutoResizingMask;
|
|
||||||
if (flags & kAutoResizeWidth) {
|
|
||||||
autoresizing_mask |= NSViewWidthSizable;
|
|
||||||
}
|
|
||||||
if (flags & kAutoResizeHeight) {
|
|
||||||
autoresizing_mask |= NSViewHeightSizable;
|
|
||||||
}
|
|
||||||
if (flags & kAutoResizeHorizontal) {
|
|
||||||
autoresizing_mask |=
|
|
||||||
NSViewMaxXMargin | NSViewMinXMargin | NSViewWidthSizable;
|
|
||||||
}
|
|
||||||
if (flags & kAutoResizeVertical) {
|
|
||||||
autoresizing_mask |=
|
|
||||||
NSViewMaxYMargin | NSViewMinYMargin | NSViewHeightSizable;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetNativeView().GetNativeNSView();
|
|
||||||
view.autoresizingMask = autoresizing_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) {
|
|
||||||
auto* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto* view = iwc_view->GetNativeView().GetNativeNSView();
|
|
||||||
const auto superview_height =
|
|
||||||
view.superview ? view.superview.frame.size.height : 0;
|
|
||||||
int y_coord = superview_height - bounds.y() - bounds.height();
|
|
||||||
|
|
||||||
view.frame = NSMakeRect(bounds.x(), y_coord, bounds.width(), bounds.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Rect NativeBrowserViewMac::GetBounds() {
|
|
||||||
auto* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return gfx::Rect();
|
|
||||||
|
|
||||||
NSView* view = iwc_view->GetNativeView().GetNativeNSView();
|
|
||||||
const int superview_height =
|
|
||||||
view.superview ? view.superview.frame.size.height : 0;
|
|
||||||
int y_coord = superview_height - view.frame.origin.y - view.frame.size.height;
|
|
||||||
|
|
||||||
return gfx::Rect(view.frame.origin.x, y_coord, view.frame.size.width,
|
|
||||||
view.frame.size.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
|
|
||||||
auto* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetNativeView().GetNativeNSView();
|
|
||||||
view.wantsLayer = YES;
|
|
||||||
view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
NativeBrowserView* NativeBrowserView::Create(
|
|
||||||
InspectableWebContents* inspectable_web_contents) {
|
|
||||||
return new NativeBrowserViewMac(inspectable_web_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace electron
|
|
|
@ -1,143 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view_views.h"
|
|
||||||
|
|
||||||
#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
|
|
||||||
#include "ui/gfx/geometry/rect.h"
|
|
||||||
#include "ui/views/background.h"
|
|
||||||
#include "ui/views/view.h"
|
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
NativeBrowserViewViews::NativeBrowserViewViews(
|
|
||||||
InspectableWebContents* inspectable_web_contents)
|
|
||||||
: NativeBrowserView(inspectable_web_contents) {}
|
|
||||||
|
|
||||||
NativeBrowserViewViews::~NativeBrowserViewViews() = default;
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::SetAutoResizeFlags(uint8_t flags) {
|
|
||||||
auto_resize_flags_ = flags;
|
|
||||||
ResetAutoResizeProportions();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::SetAutoResizeProportions(
|
|
||||||
const gfx::Size& window_size) {
|
|
||||||
if ((auto_resize_flags_ & AutoResizeFlags::kAutoResizeHorizontal) &&
|
|
||||||
!auto_horizontal_proportion_set_) {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetView();
|
|
||||||
auto view_bounds = view->bounds();
|
|
||||||
auto_horizontal_proportion_width_ =
|
|
||||||
static_cast<float>(window_size.width()) /
|
|
||||||
static_cast<float>(view_bounds.width());
|
|
||||||
auto_horizontal_proportion_left_ = static_cast<float>(window_size.width()) /
|
|
||||||
static_cast<float>(view_bounds.x());
|
|
||||||
auto_horizontal_proportion_set_ = true;
|
|
||||||
}
|
|
||||||
if ((auto_resize_flags_ & AutoResizeFlags::kAutoResizeVertical) &&
|
|
||||||
!auto_vertical_proportion_set_) {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetView();
|
|
||||||
auto view_bounds = view->bounds();
|
|
||||||
auto_vertical_proportion_height_ =
|
|
||||||
static_cast<float>(window_size.height()) /
|
|
||||||
static_cast<float>(view_bounds.height());
|
|
||||||
auto_vertical_proportion_top_ = static_cast<float>(window_size.height()) /
|
|
||||||
static_cast<float>(view_bounds.y());
|
|
||||||
auto_vertical_proportion_set_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::AutoResize(const gfx::Rect& new_window,
|
|
||||||
int width_delta,
|
|
||||||
int height_delta) {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetView();
|
|
||||||
const auto flags = GetAutoResizeFlags();
|
|
||||||
if (!(flags & kAutoResizeWidth)) {
|
|
||||||
width_delta = 0;
|
|
||||||
}
|
|
||||||
if (!(flags & kAutoResizeHeight)) {
|
|
||||||
height_delta = 0;
|
|
||||||
}
|
|
||||||
if (height_delta || width_delta) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
auto new_view_bounds = view->bounds();
|
|
||||||
if (flags & kAutoResizeHorizontal) {
|
|
||||||
new_view_bounds.set_width(new_window.width() /
|
|
||||||
auto_horizontal_proportion_width_);
|
|
||||||
new_view_bounds.set_x(new_window.width() /
|
|
||||||
auto_horizontal_proportion_left_);
|
|
||||||
}
|
|
||||||
if (flags & kAutoResizeVertical) {
|
|
||||||
new_view_bounds.set_height(new_window.height() /
|
|
||||||
auto_vertical_proportion_height_);
|
|
||||||
new_view_bounds.set_y(new_window.height() / auto_vertical_proportion_top_);
|
|
||||||
}
|
|
||||||
if ((flags & kAutoResizeHorizontal) || (flags & kAutoResizeVertical)) {
|
|
||||||
view->SetBoundsRect(new_view_bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::ResetAutoResizeProportions() {
|
|
||||||
if (auto_resize_flags_ & AutoResizeFlags::kAutoResizeHorizontal) {
|
|
||||||
auto_horizontal_proportion_set_ = false;
|
|
||||||
}
|
|
||||||
if (auto_resize_flags_ & AutoResizeFlags::kAutoResizeVertical) {
|
|
||||||
auto_vertical_proportion_set_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetView();
|
|
||||||
view->SetBoundsRect(bounds);
|
|
||||||
ResetAutoResizeProportions();
|
|
||||||
|
|
||||||
view->InvalidateLayout();
|
|
||||||
view->SchedulePaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Rect NativeBrowserViewViews::GetBounds() {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return gfx::Rect();
|
|
||||||
return iwc_view->GetView()->bounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::RenderViewReady() {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (iwc_view)
|
|
||||||
iwc_view->GetView()->Layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeBrowserViewViews::SetBackgroundColor(SkColor color) {
|
|
||||||
InspectableWebContentsView* iwc_view = GetInspectableWebContentsView();
|
|
||||||
if (!iwc_view)
|
|
||||||
return;
|
|
||||||
auto* view = iwc_view->GetView();
|
|
||||||
view->SetBackground(views::CreateSolidBackground(color));
|
|
||||||
view->SchedulePaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
NativeBrowserView* NativeBrowserView::Create(
|
|
||||||
InspectableWebContents* inspectable_web_contents) {
|
|
||||||
return new NativeBrowserViewViews(inspectable_web_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace electron
|
|
|
@ -1,49 +0,0 @@
|
||||||
// Copyright (c) 2017 GitHub, Inc.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
|
|
||||||
#define ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
|
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view.h"
|
|
||||||
|
|
||||||
namespace electron {
|
|
||||||
|
|
||||||
class NativeBrowserViewViews : public NativeBrowserView {
|
|
||||||
public:
|
|
||||||
explicit NativeBrowserViewViews(
|
|
||||||
InspectableWebContents* inspectable_web_contents);
|
|
||||||
~NativeBrowserViewViews() override;
|
|
||||||
|
|
||||||
void SetAutoResizeProportions(const gfx::Size& window_size);
|
|
||||||
void AutoResize(const gfx::Rect& new_window,
|
|
||||||
int width_delta,
|
|
||||||
int height_delta);
|
|
||||||
uint8_t GetAutoResizeFlags() { return auto_resize_flags_; }
|
|
||||||
|
|
||||||
// NativeBrowserView:
|
|
||||||
void SetAutoResizeFlags(uint8_t flags) override;
|
|
||||||
void SetBounds(const gfx::Rect& bounds) override;
|
|
||||||
gfx::Rect GetBounds() override;
|
|
||||||
void SetBackgroundColor(SkColor color) override;
|
|
||||||
|
|
||||||
// WebContentsObserver:
|
|
||||||
void RenderViewReady() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ResetAutoResizeProportions();
|
|
||||||
|
|
||||||
uint8_t auto_resize_flags_ = 0;
|
|
||||||
|
|
||||||
bool auto_horizontal_proportion_set_ = false;
|
|
||||||
float auto_horizontal_proportion_width_ = 0.;
|
|
||||||
float auto_horizontal_proportion_left_ = 0.;
|
|
||||||
|
|
||||||
bool auto_vertical_proportion_set_ = false;
|
|
||||||
float auto_vertical_proportion_height_ = 0.;
|
|
||||||
float auto_vertical_proportion_top_ = 0.;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace electron
|
|
||||||
|
|
||||||
#endif // ELECTRON_SHELL_BROWSER_NATIVE_BROWSER_VIEW_VIEWS_H_
|
|
|
@ -92,6 +92,8 @@ gfx::Size GetExpandedWindowSize(const NativeWindow* window, gfx::Size size) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
const char kElectronNativeWindowKey[] = "__ELECTRON_NATIVE_WINDOW__";
|
||||||
|
|
||||||
NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||||
NativeWindow* parent)
|
NativeWindow* parent)
|
||||||
: widget_(std::make_unique<views::Widget>()), parent_(parent) {
|
: widget_(std::make_unique<views::Widget>()), parent_(parent) {
|
||||||
|
|
|
@ -46,9 +46,10 @@ class PersistentDictionary;
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
|
extern const char kElectronNativeWindowKey[];
|
||||||
|
|
||||||
class ElectronMenuModel;
|
class ElectronMenuModel;
|
||||||
class BackgroundThrottlingSource;
|
class BackgroundThrottlingSource;
|
||||||
class NativeBrowserView;
|
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
class BrowserView;
|
class BrowserView;
|
||||||
|
@ -186,9 +187,6 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual bool IsFocusable();
|
virtual bool IsFocusable();
|
||||||
virtual void SetMenu(ElectronMenuModel* menu);
|
virtual void SetMenu(ElectronMenuModel* menu);
|
||||||
virtual void SetParentWindow(NativeWindow* parent);
|
virtual void SetParentWindow(NativeWindow* parent);
|
||||||
virtual void AddBrowserView(NativeBrowserView* browser_view) = 0;
|
|
||||||
virtual void RemoveBrowserView(NativeBrowserView* browser_view) = 0;
|
|
||||||
virtual void SetTopBrowserView(NativeBrowserView* browser_view) = 0;
|
|
||||||
virtual content::DesktopMediaID GetDesktopMediaID() const = 0;
|
virtual content::DesktopMediaID GetDesktopMediaID() const = 0;
|
||||||
virtual gfx::NativeView GetNativeView() const = 0;
|
virtual gfx::NativeView GetNativeView() const = 0;
|
||||||
virtual gfx::NativeWindow GetNativeWindow() const = 0;
|
virtual gfx::NativeWindow GetNativeWindow() const = 0;
|
||||||
|
@ -389,8 +387,6 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
NativeWindow* parent() const { return parent_; }
|
NativeWindow* parent() const { return parent_; }
|
||||||
bool is_modal() const { return is_modal_; }
|
bool is_modal() const { return is_modal_; }
|
||||||
|
|
||||||
std::list<NativeBrowserView*> browser_views() const { return browser_views_; }
|
|
||||||
|
|
||||||
int32_t window_id() const { return next_id_; }
|
int32_t window_id() const { return next_id_; }
|
||||||
|
|
||||||
void add_child_window(NativeWindow* child) {
|
void add_child_window(NativeWindow* child) {
|
||||||
|
@ -426,14 +422,6 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
|
|
||||||
void set_content_view(views::View* view) { content_view_ = view; }
|
void set_content_view(views::View* view) { content_view_ = view; }
|
||||||
|
|
||||||
void add_browser_view(NativeBrowserView* browser_view) {
|
|
||||||
browser_views_.push_back(browser_view);
|
|
||||||
}
|
|
||||||
void remove_browser_view(NativeBrowserView* browser_view) {
|
|
||||||
browser_views_.remove_if(
|
|
||||||
[&browser_view](NativeBrowserView* n) { return (n == browser_view); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// The boolean parsing of the "titleBarOverlay" option
|
// The boolean parsing of the "titleBarOverlay" option
|
||||||
bool titlebar_overlay_ = false;
|
bool titlebar_overlay_ = false;
|
||||||
|
|
||||||
|
@ -500,9 +488,6 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
// Is this a modal window.
|
// Is this a modal window.
|
||||||
bool is_modal_ = false;
|
bool is_modal_ = false;
|
||||||
|
|
||||||
// The browser view layer.
|
|
||||||
std::list<NativeBrowserView*> browser_views_;
|
|
||||||
|
|
||||||
std::list<DraggableRegionProvider*> draggable_region_providers_;
|
std::list<DraggableRegionProvider*> draggable_region_providers_;
|
||||||
|
|
||||||
// Observers of this window.
|
// Observers of this window.
|
||||||
|
|
|
@ -110,9 +110,6 @@ class NativeWindowMac : public NativeWindow,
|
||||||
void SetContentProtection(bool enable) override;
|
void SetContentProtection(bool enable) override;
|
||||||
void SetFocusable(bool focusable) override;
|
void SetFocusable(bool focusable) override;
|
||||||
bool IsFocusable() override;
|
bool IsFocusable() override;
|
||||||
void AddBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void RemoveBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void SetTopBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void SetParentWindow(NativeWindow* parent) override;
|
void SetParentWindow(NativeWindow* parent) override;
|
||||||
content::DesktopMediaID GetDesktopMediaID() const override;
|
content::DesktopMediaID GetDesktopMediaID() const override;
|
||||||
gfx::NativeView GetNativeView() const override;
|
gfx::NativeView GetNativeView() const override;
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "content/public/browser/desktop_media_id.h"
|
#include "content/public/browser/desktop_media_id.h"
|
||||||
#include "shell/browser/browser.h"
|
#include "shell/browser/browser.h"
|
||||||
#include "shell/browser/javascript_environment.h"
|
#include "shell/browser/javascript_environment.h"
|
||||||
#include "shell/browser/native_browser_view_mac.h"
|
|
||||||
#include "shell/browser/ui/cocoa/electron_native_widget_mac.h"
|
#include "shell/browser/ui/cocoa/electron_native_widget_mac.h"
|
||||||
#include "shell/browser/ui/cocoa/electron_ns_window.h"
|
#include "shell/browser/ui/cocoa/electron_ns_window.h"
|
||||||
#include "shell/browser/ui/cocoa/electron_ns_window_delegate.h"
|
#include "shell/browser/ui/cocoa/electron_ns_window_delegate.h"
|
||||||
|
@ -206,14 +205,6 @@ void ReorderChildWindowAbove(NSWindow* child_window, NSWindow* other_window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSView* GetNativeNSView(NativeBrowserView* view) {
|
|
||||||
if (auto* inspectable = view->GetInspectableWebContentsView()) {
|
|
||||||
return inspectable->GetNativeView().GetNativeNSView();
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||||
|
@ -296,6 +287,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||||
params.native_widget =
|
params.native_widget =
|
||||||
new ElectronNativeWidgetMac(this, windowType, styleMask, widget());
|
new ElectronNativeWidgetMac(this, windowType, styleMask, widget());
|
||||||
widget()->Init(std::move(params));
|
widget()->Init(std::move(params));
|
||||||
|
widget()->SetNativeWindowProperty(kElectronNativeWindowKey, this);
|
||||||
SetCanResize(resizable);
|
SetCanResize(resizable);
|
||||||
window_ = static_cast<ElectronNSWindow*>(
|
window_ = static_cast<ElectronNSWindow*>(
|
||||||
widget()->GetNativeWindow().GetNativeNSWindow());
|
widget()->GetNativeWindow().GetNativeNSWindow());
|
||||||
|
@ -1285,64 +1277,6 @@ bool NativeWindowMac::IsFocusable() {
|
||||||
return ![window_ disableKeyOrMainWindow];
|
return ![window_ disableKeyOrMainWindow];
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowMac::AddBrowserView(NativeBrowserView* view) {
|
|
||||||
[CATransaction begin];
|
|
||||||
[CATransaction setDisableActions:YES];
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
[CATransaction commit];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_browser_view(view);
|
|
||||||
if (auto* native_view = GetNativeNSView(view)) {
|
|
||||||
[[window_ contentView] addSubview:native_view
|
|
||||||
positioned:NSWindowAbove
|
|
||||||
relativeTo:nil];
|
|
||||||
native_view.hidden = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CATransaction commit];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
|
|
||||||
[CATransaction begin];
|
|
||||||
[CATransaction setDisableActions:YES];
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
[CATransaction commit];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto* native_view = GetNativeNSView(view)) {
|
|
||||||
[native_view removeFromSuperview];
|
|
||||||
}
|
|
||||||
remove_browser_view(view);
|
|
||||||
|
|
||||||
[CATransaction commit];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowMac::SetTopBrowserView(NativeBrowserView* view) {
|
|
||||||
[CATransaction begin];
|
|
||||||
[CATransaction setDisableActions:YES];
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
[CATransaction commit];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_browser_view(view);
|
|
||||||
add_browser_view(view);
|
|
||||||
if (auto* native_view = GetNativeNSView(view)) {
|
|
||||||
[[window_ contentView] addSubview:native_view
|
|
||||||
positioned:NSWindowAbove
|
|
||||||
relativeTo:nil];
|
|
||||||
native_view.hidden = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CATransaction commit];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
|
void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
|
||||||
InternalSetParentWindow(parent, IsVisible());
|
InternalSetParentWindow(parent, IsVisible());
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "content/public/browser/desktop_media_id.h"
|
#include "content/public/browser/desktop_media_id.h"
|
||||||
#include "shell/browser/api/electron_api_web_contents.h"
|
#include "shell/browser/api/electron_api_web_contents.h"
|
||||||
#include "shell/browser/native_browser_view_views.h"
|
|
||||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
#include "shell/browser/ui/inspectable_web_contents.h"
|
||||||
#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
|
#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
|
||||||
#include "shell/browser/ui/views/root_view.h"
|
#include "shell/browser/ui/views/root_view.h"
|
||||||
|
@ -333,6 +332,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
widget()->Init(std::move(params));
|
widget()->Init(std::move(params));
|
||||||
|
widget()->SetNativeWindowProperty(kElectronNativeWindowKey, this);
|
||||||
SetCanResize(resizable_);
|
SetCanResize(resizable_);
|
||||||
|
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
|
@ -1361,50 +1361,6 @@ void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeWindowViews::AddBrowserView(NativeBrowserView* view) {
|
|
||||||
if (!content_view())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_browser_view(view);
|
|
||||||
if (view->GetInspectableWebContentsView())
|
|
||||||
content_view()->AddChildView(
|
|
||||||
view->GetInspectableWebContentsView()->GetView());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
|
|
||||||
if (!content_view())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (view->GetInspectableWebContentsView())
|
|
||||||
content_view()->RemoveChildView(
|
|
||||||
view->GetInspectableWebContentsView()->GetView());
|
|
||||||
remove_browser_view(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowViews::SetTopBrowserView(NativeBrowserView* view) {
|
|
||||||
if (!content_view())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_browser_view(view);
|
|
||||||
add_browser_view(view);
|
|
||||||
|
|
||||||
if (view->GetInspectableWebContentsView())
|
|
||||||
content_view()->ReorderChildView(
|
|
||||||
view->GetInspectableWebContentsView()->GetView(), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeWindowViews::SetParentWindow(NativeWindow* parent) {
|
void NativeWindowViews::SetParentWindow(NativeWindow* parent) {
|
||||||
NativeWindow::SetParentWindow(parent);
|
NativeWindow::SetParentWindow(parent);
|
||||||
|
|
||||||
|
@ -1698,14 +1654,6 @@ void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget,
|
||||||
// handle minimized windows on Windows.
|
// handle minimized windows on Windows.
|
||||||
const auto new_bounds = GetBounds();
|
const auto new_bounds = GetBounds();
|
||||||
if (widget_size_ != new_bounds.size()) {
|
if (widget_size_ != new_bounds.size()) {
|
||||||
int width_delta = new_bounds.width() - widget_size_.width();
|
|
||||||
int height_delta = new_bounds.height() - widget_size_.height();
|
|
||||||
for (NativeBrowserView* item : browser_views()) {
|
|
||||||
auto* native_view = static_cast<NativeBrowserViewViews*>(item);
|
|
||||||
native_view->SetAutoResizeProportions(widget_size_);
|
|
||||||
native_view->AutoResize(new_bounds, width_delta, height_delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifyWindowResize();
|
NotifyWindowResize();
|
||||||
widget_size_ = new_bounds.size();
|
widget_size_ = new_bounds.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,9 +124,6 @@ class NativeWindowViews : public NativeWindow,
|
||||||
void SetFocusable(bool focusable) override;
|
void SetFocusable(bool focusable) override;
|
||||||
bool IsFocusable() override;
|
bool IsFocusable() override;
|
||||||
void SetMenu(ElectronMenuModel* menu_model) override;
|
void SetMenu(ElectronMenuModel* menu_model) override;
|
||||||
void AddBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void RemoveBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void SetTopBrowserView(NativeBrowserView* browser_view) override;
|
|
||||||
void SetParentWindow(NativeWindow* parent) override;
|
void SetParentWindow(NativeWindow* parent) override;
|
||||||
gfx::NativeView GetNativeView() const override;
|
gfx::NativeView GetNativeView() const override;
|
||||||
gfx::NativeWindow GetNativeWindow() const override;
|
gfx::NativeWindow GetNativeWindow() const override;
|
||||||
|
|
|
@ -15,9 +15,13 @@ DelayedNativeViewHost::~DelayedNativeViewHost() = default;
|
||||||
|
|
||||||
void DelayedNativeViewHost::ViewHierarchyChanged(
|
void DelayedNativeViewHost::ViewHierarchyChanged(
|
||||||
const views::ViewHierarchyChangedDetails& details) {
|
const views::ViewHierarchyChangedDetails& details) {
|
||||||
NativeViewHost::ViewHierarchyChanged(details);
|
// NativeViewHost doesn't expect to have children, so filter the
|
||||||
if (details.is_add && GetWidget())
|
// ViewHierarchyChanged events before passing them on.
|
||||||
Attach(native_view_);
|
if (details.child == this) {
|
||||||
|
NativeViewHost::ViewHierarchyChanged(details);
|
||||||
|
if (details.is_add && GetWidget() && !native_view())
|
||||||
|
Attach(native_view_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DelayedNativeViewHost::OnMousePressed(const ui::MouseEvent& ui_event) {
|
bool DelayedNativeViewHost::OnMousePressed(const ui::MouseEvent& ui_event) {
|
||||||
|
|
|
@ -22,7 +22,6 @@ class RootViewMac : public views::View {
|
||||||
RootViewMac& operator=(const RootViewMac&) = delete;
|
RootViewMac& operator=(const RootViewMac&) = delete;
|
||||||
|
|
||||||
// views::View:
|
// views::View:
|
||||||
void Layout() override;
|
|
||||||
gfx::Size GetMinimumSize() const override;
|
gfx::Size GetMinimumSize() const override;
|
||||||
gfx::Size GetMaximumSize() const override;
|
gfx::Size GetMaximumSize() const override;
|
||||||
|
|
||||||
|
|
|
@ -4,23 +4,20 @@
|
||||||
|
|
||||||
#include "shell/browser/ui/cocoa/root_view_mac.h"
|
#include "shell/browser/ui/cocoa/root_view_mac.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "shell/browser/native_window.h"
|
#include "shell/browser/native_window.h"
|
||||||
|
#include "ui/views/layout/fill_layout.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
RootViewMac::RootViewMac(NativeWindow* window) : window_(window) {
|
RootViewMac::RootViewMac(NativeWindow* window) : window_(window) {
|
||||||
set_owned_by_client();
|
set_owned_by_client();
|
||||||
|
SetLayoutManager(std::make_unique<views::FillLayout>());
|
||||||
}
|
}
|
||||||
|
|
||||||
RootViewMac::~RootViewMac() = default;
|
RootViewMac::~RootViewMac() = default;
|
||||||
|
|
||||||
void RootViewMac::Layout() {
|
|
||||||
if (!window_->content_view()) // Not ready yet.
|
|
||||||
return;
|
|
||||||
|
|
||||||
window_->content_view()->SetBoundsRect(gfx::Rect(gfx::Point(), size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Size RootViewMac::GetMinimumSize() const {
|
gfx::Size RootViewMac::GetMinimumSize() const {
|
||||||
return window_->GetMinimumSize();
|
return window_->GetMinimumSize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include "shell/browser/ui/views/frameless_view.h"
|
#include "shell/browser/ui/views/frameless_view.h"
|
||||||
|
|
||||||
#include "shell/browser/native_browser_view_views.h"
|
|
||||||
#include "shell/browser/native_window_views.h"
|
#include "shell/browser/native_window_views.h"
|
||||||
#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
|
#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
|
||||||
#include "ui/aura/window.h"
|
#include "ui/aura/window.h"
|
||||||
|
|
|
@ -21,10 +21,6 @@ bool IsFakeLocationProviderEnabled() {
|
||||||
return BUILDFLAG(OVERRIDE_LOCATION_PROVIDER);
|
return BUILDFLAG(OVERRIDE_LOCATION_PROVIDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsViewApiEnabled() {
|
|
||||||
return BUILDFLAG(ENABLE_VIEWS_API);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPrintingEnabled() {
|
bool IsPrintingEnabled() {
|
||||||
return BUILDFLAG(ENABLE_PRINTING);
|
return BUILDFLAG(ENABLE_PRINTING);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +46,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
|
dict.SetMethod("isPDFViewerEnabled", &IsPDFViewerEnabled);
|
||||||
dict.SetMethod("isFakeLocationProviderEnabled",
|
dict.SetMethod("isFakeLocationProviderEnabled",
|
||||||
&IsFakeLocationProviderEnabled);
|
&IsFakeLocationProviderEnabled);
|
||||||
dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
|
|
||||||
dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled);
|
dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled);
|
||||||
dict.SetMethod("isComponentBuild", &IsComponentBuild);
|
dict.SetMethod("isComponentBuild", &IsComponentBuild);
|
||||||
dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled);
|
dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled);
|
||||||
|
|
|
@ -9,6 +9,15 @@
|
||||||
|
|
||||||
#include "third_party/skia/include/core/SkColor.h"
|
#include "third_party/skia/include/core/SkColor.h"
|
||||||
|
|
||||||
|
// SkColor is a typedef for uint32_t, this wrapper is to tag an SkColor for
|
||||||
|
// ease of use in gin converters.
|
||||||
|
struct WrappedSkColor {
|
||||||
|
WrappedSkColor() {}
|
||||||
|
WrappedSkColor(SkColor c) : value(c) {} // NOLINT(runtime/explicit)
|
||||||
|
SkColor value;
|
||||||
|
operator SkColor() const { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
// Parses a CSS-style color string from hex, rgb(), rgba(),
|
// Parses a CSS-style color string from hex, rgb(), rgba(),
|
||||||
|
|
|
@ -4,9 +4,14 @@
|
||||||
|
|
||||||
#include "shell/common/gin_converters/gfx_converter.h"
|
#include "shell/common/gin_converters/gfx_converter.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gin/data_object_builder.h"
|
||||||
|
#include "shell/common/color_util.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "ui/display/display.h"
|
#include "ui/display/display.h"
|
||||||
#include "ui/display/screen.h"
|
#include "ui/display/screen.h"
|
||||||
|
#include "ui/gfx/geometry/insets.h"
|
||||||
#include "ui/gfx/geometry/point.h"
|
#include "ui/gfx/geometry/point.h"
|
||||||
#include "ui/gfx/geometry/point_f.h"
|
#include "ui/gfx/geometry/point_f.h"
|
||||||
#include "ui/gfx/geometry/rect.h"
|
#include "ui/gfx/geometry/rect.h"
|
||||||
|
@ -103,6 +108,35 @@ bool Converter<gfx::Rect>::FromV8(v8::Isolate* isolate,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> Converter<gfx::Insets>::ToV8(v8::Isolate* isolate,
|
||||||
|
const gfx::Insets& val) {
|
||||||
|
return gin::DataObjectBuilder(isolate)
|
||||||
|
.Set("top", val.top())
|
||||||
|
.Set("left", val.left())
|
||||||
|
.Set("bottom", val.bottom())
|
||||||
|
.Set("right", val.right())
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Converter<gfx::Insets>::FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
gfx::Insets* out) {
|
||||||
|
gin::Dictionary dict(isolate);
|
||||||
|
if (!gin::ConvertFromV8(isolate, val, &dict))
|
||||||
|
return false;
|
||||||
|
double top, left, right, bottom;
|
||||||
|
if (!dict.Get("top", &top))
|
||||||
|
return false;
|
||||||
|
if (!dict.Get("left", &left))
|
||||||
|
return false;
|
||||||
|
if (!dict.Get("bottom", &bottom))
|
||||||
|
return false;
|
||||||
|
if (!dict.Get("right", &right))
|
||||||
|
return false;
|
||||||
|
*out = gfx::Insets::TLBR(top, left, bottom, right);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct Converter<display::Display::AccelerometerSupport> {
|
struct Converter<display::Display::AccelerometerSupport> {
|
||||||
static v8::Local<v8::Value> ToV8(
|
static v8::Local<v8::Value> ToV8(
|
||||||
|
@ -185,4 +219,14 @@ v8::Local<v8::Value> Converter<gfx::ResizeEdge>::ToV8(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Converter<WrappedSkColor>::FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
WrappedSkColor* out) {
|
||||||
|
std::string str;
|
||||||
|
if (!gin::ConvertFromV8(isolate, val, &str))
|
||||||
|
return false;
|
||||||
|
*out = electron::ParseCSSColor(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace gin
|
} // namespace gin
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GFX_CONVERTER_H_
|
#define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GFX_CONVERTER_H_
|
||||||
|
|
||||||
#include "gin/converter.h"
|
#include "gin/converter.h"
|
||||||
|
#include "shell/common/color_util.h"
|
||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
class Display;
|
class Display;
|
||||||
|
@ -16,6 +17,7 @@ class Point;
|
||||||
class PointF;
|
class PointF;
|
||||||
class Size;
|
class Size;
|
||||||
class Rect;
|
class Rect;
|
||||||
|
class Insets;
|
||||||
enum class ResizeEdge;
|
enum class ResizeEdge;
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
|
|
||||||
|
@ -54,6 +56,15 @@ struct Converter<gfx::Rect> {
|
||||||
gfx::Rect* out);
|
gfx::Rect* out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<gfx::Insets> {
|
||||||
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
const gfx::Insets& val);
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
gfx::Insets* out);
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct Converter<display::Display> {
|
struct Converter<display::Display> {
|
||||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
@ -69,6 +80,13 @@ struct Converter<gfx::ResizeEdge> {
|
||||||
const gfx::ResizeEdge& val);
|
const gfx::ResizeEdge& val);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<WrappedSkColor> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
WrappedSkColor* out);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace gin
|
} // namespace gin
|
||||||
|
|
||||||
#endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GFX_CONVERTER_H_
|
#endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GFX_CONVERTER_H_
|
||||||
|
|
|
@ -23,7 +23,7 @@ class EventEmitterMixin;
|
||||||
// class Example : public gin::Wrappable<Example>,
|
// class Example : public gin::Wrappable<Example>,
|
||||||
// public gin_helper::Constructible<Example> {
|
// public gin_helper::Constructible<Example> {
|
||||||
// public:
|
// public:
|
||||||
// static gin::Handle<Tray> New(...usual gin method arguments...);
|
// static gin::Handle<Example> New(...usual gin method arguments...);
|
||||||
// static void FillObjectTemplate(
|
// static void FillObjectTemplate(
|
||||||
// v8::Isolate*,
|
// v8::Isolate*,
|
||||||
// v8::Local<v8::ObjectTemplate>);
|
// v8::Local<v8::ObjectTemplate>);
|
||||||
|
|
|
@ -46,13 +46,13 @@
|
||||||
#define ELECTRON_BROWSER_BINDINGS(V) \
|
#define ELECTRON_BROWSER_BINDINGS(V) \
|
||||||
V(electron_browser_app) \
|
V(electron_browser_app) \
|
||||||
V(electron_browser_auto_updater) \
|
V(electron_browser_auto_updater) \
|
||||||
V(electron_browser_browser_view) \
|
|
||||||
V(electron_browser_content_tracing) \
|
V(electron_browser_content_tracing) \
|
||||||
V(electron_browser_crash_reporter) \
|
V(electron_browser_crash_reporter) \
|
||||||
V(electron_browser_desktop_capturer) \
|
V(electron_browser_desktop_capturer) \
|
||||||
V(electron_browser_dialog) \
|
V(electron_browser_dialog) \
|
||||||
V(electron_browser_event_emitter) \
|
V(electron_browser_event_emitter) \
|
||||||
V(electron_browser_global_shortcut) \
|
V(electron_browser_global_shortcut) \
|
||||||
|
V(electron_browser_image_view) \
|
||||||
V(electron_browser_in_app_purchase) \
|
V(electron_browser_in_app_purchase) \
|
||||||
V(electron_browser_menu) \
|
V(electron_browser_menu) \
|
||||||
V(electron_browser_message_port) \
|
V(electron_browser_message_port) \
|
||||||
|
@ -98,8 +98,6 @@
|
||||||
|
|
||||||
#define ELECTRON_UTILITY_BINDINGS(V) V(electron_utility_parent_port)
|
#define ELECTRON_UTILITY_BINDINGS(V) V(electron_utility_parent_port)
|
||||||
|
|
||||||
#define ELECTRON_VIEWS_BINDINGS(V) V(electron_browser_image_view)
|
|
||||||
|
|
||||||
#define ELECTRON_TESTING_BINDINGS(V) V(electron_common_testing)
|
#define ELECTRON_TESTING_BINDINGS(V) V(electron_common_testing)
|
||||||
|
|
||||||
// This is used to load built-in bindings. Instead of using
|
// This is used to load built-in bindings. Instead of using
|
||||||
|
@ -112,9 +110,6 @@ ELECTRON_BROWSER_BINDINGS(V)
|
||||||
ELECTRON_COMMON_BINDINGS(V)
|
ELECTRON_COMMON_BINDINGS(V)
|
||||||
ELECTRON_RENDERER_BINDINGS(V)
|
ELECTRON_RENDERER_BINDINGS(V)
|
||||||
ELECTRON_UTILITY_BINDINGS(V)
|
ELECTRON_UTILITY_BINDINGS(V)
|
||||||
#if BUILDFLAG(ENABLE_VIEWS_API)
|
|
||||||
ELECTRON_VIEWS_BINDINGS(V)
|
|
||||||
#endif
|
|
||||||
#if DCHECK_IS_ON()
|
#if DCHECK_IS_ON()
|
||||||
ELECTRON_TESTING_BINDINGS(V)
|
ELECTRON_TESTING_BINDINGS(V)
|
||||||
#endif
|
#endif
|
||||||
|
@ -441,9 +436,6 @@ void NodeBindings::RegisterBuiltinBindings() {
|
||||||
#define V(modname) _register_##modname();
|
#define V(modname) _register_##modname();
|
||||||
if (IsBrowserProcess()) {
|
if (IsBrowserProcess()) {
|
||||||
ELECTRON_BROWSER_BINDINGS(V)
|
ELECTRON_BROWSER_BINDINGS(V)
|
||||||
#if BUILDFLAG(ENABLE_VIEWS_API)
|
|
||||||
ELECTRON_VIEWS_BINDINGS(V)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
ELECTRON_COMMON_BINDINGS(V)
|
ELECTRON_COMMON_BINDINGS(V)
|
||||||
if (IsRendererProcess()) {
|
if (IsRendererProcess()) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('BrowserView module', () => {
|
||||||
let view: BrowserView;
|
let view: BrowserView;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
expect(webContents.getAllWebContents()).to.have.length(0);
|
expect(webContents.getAllWebContents().length).to.equal(0, 'expected no webContents to exist');
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
width: 400,
|
width: 400,
|
||||||
|
@ -37,7 +37,7 @@ describe('BrowserView module', () => {
|
||||||
await p;
|
await p;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(webContents.getAllWebContents()).to.have.length(0);
|
expect(webContents.getAllWebContents().length).to.equal(0, 'expected no webContents to exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the correct class name on the prototype', () => {
|
it('sets the correct class name on the prototype', () => {
|
||||||
|
@ -49,21 +49,28 @@ describe('BrowserView module', () => {
|
||||||
await wc.loadURL('about:blank');
|
await wc.loadURL('about:blank');
|
||||||
|
|
||||||
view = new BrowserView({ webContents: wc } as any);
|
view = new BrowserView({ webContents: wc } as any);
|
||||||
|
expect(view.webContents === wc).to.be.true('view.webContents === wc');
|
||||||
|
|
||||||
expect(view.webContents.getURL()).to.equal('about:blank');
|
expect(view.webContents.getURL()).to.equal('about:blank');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('has type browserView', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
expect(view.webContents.getType()).to.equal('browserView');
|
||||||
|
});
|
||||||
|
|
||||||
describe('BrowserView.setBackgroundColor()', () => {
|
describe('BrowserView.setBackgroundColor()', () => {
|
||||||
it('does not throw for valid args', () => {
|
it('does not throw for valid args', () => {
|
||||||
view = new BrowserView();
|
view = new BrowserView();
|
||||||
view.setBackgroundColor('#000');
|
view.setBackgroundColor('#000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws for invalid args', () => {
|
// We now treat invalid args as "no background".
|
||||||
|
it('does not throw for invalid args', () => {
|
||||||
view = new BrowserView();
|
view = new BrowserView();
|
||||||
expect(() => {
|
expect(() => {
|
||||||
view.setBackgroundColor(null as any);
|
view.setBackgroundColor({} as any);
|
||||||
}).to.throw(/conversion failure/);
|
}).not.to.throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Linux and arm64 platforms (WOA and macOS) do not return any capture sources
|
// Linux and arm64 platforms (WOA and macOS) do not return any capture sources
|
||||||
|
@ -128,7 +135,148 @@ describe('BrowserView module', () => {
|
||||||
view = new BrowserView();
|
view = new BrowserView();
|
||||||
expect(() => {
|
expect(() => {
|
||||||
view.setAutoResize(null as any);
|
view.setAutoResize(null as any);
|
||||||
}).to.throw(/conversion failure/);
|
}).to.throw(/Invalid auto resize options/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not resize when the BrowserView has no AutoResize', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 400, height: 200 });
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resizes horizontally when the window is resized horizontally', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ width: true, height: false });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 400, height: 200 });
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 800,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resizes vertically when the window is resized vertically', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ width: false, height: true });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 200, height: 400 });
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 400
|
||||||
|
});
|
||||||
|
w.setSize(400, 800);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 800
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resizes both vertically and horizontally when the window is resized', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ width: true, height: true });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 400, height: 400 });
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 400
|
||||||
|
});
|
||||||
|
w.setSize(800, 800);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 800,
|
||||||
|
height: 800
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resizes proportionally', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ width: true, height: false });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 0, y: 0, width: 200, height: 100 });
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 100
|
||||||
|
});
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 600,
|
||||||
|
height: 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not move x if horizontal: false', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ width: true });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 200, y: 0, width: 200, height: 100 });
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 200,
|
||||||
|
y: 0,
|
||||||
|
width: 600,
|
||||||
|
height: 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('moves x if horizontal: true', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ horizontal: true });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 200, y: 0, width: 200, height: 100 });
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 400,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 100
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('moves x if horizontal: true width: true', () => {
|
||||||
|
view = new BrowserView();
|
||||||
|
view.setAutoResize({ horizontal: true, width: true });
|
||||||
|
w.addBrowserView(view);
|
||||||
|
view.setBounds({ x: 200, y: 0, width: 200, height: 100 });
|
||||||
|
w.setSize(800, 400);
|
||||||
|
expect(view.getBounds()).to.deep.equal({
|
||||||
|
x: 400,
|
||||||
|
y: 0,
|
||||||
|
width: 400,
|
||||||
|
height: 100
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -288,6 +436,16 @@ describe('BrowserView module', () => {
|
||||||
w2.close();
|
w2.close();
|
||||||
w2.destroy();
|
w2.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not cause a crash when used for view with destroyed web contents', async () => {
|
||||||
|
const w2 = new BrowserWindow({ show: false });
|
||||||
|
const view = new BrowserView();
|
||||||
|
view.webContents.close();
|
||||||
|
w2.addBrowserView(view);
|
||||||
|
w2.webContents.loadURL('about:blank');
|
||||||
|
await once(w2.webContents, 'did-finish-load');
|
||||||
|
w2.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BrowserWindow.removeBrowserView()', () => {
|
describe('BrowserWindow.removeBrowserView()', () => {
|
||||||
|
|
|
@ -149,7 +149,7 @@ describe('BrowserWindow module', () => {
|
||||||
it('should emit beforeunload handler', async () => {
|
it('should emit beforeunload handler', async () => {
|
||||||
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
||||||
w.close();
|
w.close();
|
||||||
await once(w.webContents, 'before-unload-fired');
|
await once(w.webContents, '-before-unload-fired');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not crash when keyboard event is sent before closing', async () => {
|
it('should not crash when keyboard event is sent before closing', async () => {
|
||||||
|
@ -241,7 +241,7 @@ describe('BrowserWindow module', () => {
|
||||||
it('should emit beforeunload event', async function () {
|
it('should emit beforeunload event', async function () {
|
||||||
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
||||||
w.webContents.executeJavaScript('window.close()', true);
|
w.webContents.executeJavaScript('window.close()', true);
|
||||||
await once(w.webContents, 'before-unload-fired');
|
await once(w.webContents, '-before-unload-fired');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4052,14 +4052,14 @@ describe('BrowserWindow module', () => {
|
||||||
it('returning false would prevent close', async () => {
|
it('returning false would prevent close', async () => {
|
||||||
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html'));
|
||||||
w.close();
|
w.close();
|
||||||
const [, proceed] = await once(w.webContents, 'before-unload-fired');
|
const [, proceed] = await once(w.webContents, '-before-unload-fired');
|
||||||
expect(proceed).to.equal(false);
|
expect(proceed).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returning empty string would prevent close', async () => {
|
it('returning empty string would prevent close', async () => {
|
||||||
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-empty-string.html'));
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-empty-string.html'));
|
||||||
w.close();
|
w.close();
|
||||||
const [, proceed] = await once(w.webContents, 'before-unload-fired');
|
const [, proceed] = await once(w.webContents, '-before-unload-fired');
|
||||||
expect(proceed).to.equal(false);
|
expect(proceed).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4075,9 +4075,9 @@ describe('BrowserWindow module', () => {
|
||||||
// the SuddenTerminationStatus message have been flushed.
|
// the SuddenTerminationStatus message have been flushed.
|
||||||
await once(w.webContents, 'console-message');
|
await once(w.webContents, 'console-message');
|
||||||
w.close();
|
w.close();
|
||||||
await once(w.webContents, 'before-unload-fired');
|
await once(w.webContents, '-before-unload-fired');
|
||||||
w.close();
|
w.close();
|
||||||
await once(w.webContents, 'before-unload-fired');
|
await once(w.webContents, '-before-unload-fired');
|
||||||
|
|
||||||
w.webContents.removeListener('destroyed', destroyListener);
|
w.webContents.removeListener('destroyed', destroyListener);
|
||||||
const wait = once(w, 'closed');
|
const wait = once(w, 'closed');
|
||||||
|
@ -4097,7 +4097,7 @@ describe('BrowserWindow module', () => {
|
||||||
// the SuddenTerminationStatus message have been flushed.
|
// the SuddenTerminationStatus message have been flushed.
|
||||||
await once(w.webContents, 'console-message');
|
await once(w.webContents, 'console-message');
|
||||||
w.reload();
|
w.reload();
|
||||||
// Chromium does not emit 'before-unload-fired' on WebContents for
|
// Chromium does not emit '-before-unload-fired' on WebContents for
|
||||||
// navigations, so we have to use other ways to know if beforeunload
|
// navigations, so we have to use other ways to know if beforeunload
|
||||||
// is fired.
|
// is fired.
|
||||||
await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
||||||
|
@ -4121,7 +4121,7 @@ describe('BrowserWindow module', () => {
|
||||||
// the SuddenTerminationStatus message have been flushed.
|
// the SuddenTerminationStatus message have been flushed.
|
||||||
await once(w.webContents, 'console-message');
|
await once(w.webContents, 'console-message');
|
||||||
w.loadURL('about:blank');
|
w.loadURL('about:blank');
|
||||||
// Chromium does not emit 'before-unload-fired' on WebContents for
|
// Chromium does not emit '-before-unload-fired' on WebContents for
|
||||||
// navigations, so we have to use other ways to know if beforeunload
|
// navigations, so we have to use other ways to know if beforeunload
|
||||||
// is fired.
|
// is fired.
|
||||||
await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
||||||
|
|
|
@ -1,18 +1,39 @@
|
||||||
import { closeWindow } from './lib/window-helpers';
|
import { closeAllWindows } from './lib/window-helpers';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import { BaseWindow, WebContentsView } from 'electron/main';
|
import { BaseWindow, View, WebContentsView } from 'electron/main';
|
||||||
|
import { once } from 'node:events';
|
||||||
|
|
||||||
describe('WebContentsView', () => {
|
describe('WebContentsView', () => {
|
||||||
let w: BaseWindow;
|
afterEach(closeAllWindows);
|
||||||
|
|
||||||
afterEach(async () => {
|
it('can be instantiated with no arguments', () => {
|
||||||
await closeWindow(w as any);
|
// eslint-disable-next-line no-new
|
||||||
w = null as unknown as BaseWindow;
|
new WebContentsView();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be instantiated with no webPreferences', () => {
|
||||||
|
// eslint-disable-next-line no-new
|
||||||
|
new WebContentsView({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be used as content view', () => {
|
it('can be used as content view', () => {
|
||||||
w = new BaseWindow({ show: false });
|
const w = new BaseWindow({ show: false });
|
||||||
w.setContentView(new WebContentsView({}));
|
w.setContentView(new WebContentsView());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be removed after a close', async () => {
|
||||||
|
const w = new BaseWindow({ show: false });
|
||||||
|
const v = new View();
|
||||||
|
const wcv = new WebContentsView();
|
||||||
|
w.setContentView(v);
|
||||||
|
v.addChildView(wcv);
|
||||||
|
await wcv.webContents.loadURL('about:blank');
|
||||||
|
const destroyed = once(wcv.webContents, 'destroyed');
|
||||||
|
wcv.webContents.executeJavaScript('window.close()');
|
||||||
|
await destroyed;
|
||||||
|
expect(wcv.webContents.isDestroyed()).to.be.true();
|
||||||
|
v.removeChildView(wcv);
|
||||||
});
|
});
|
||||||
|
|
||||||
function triggerGCByAllocation () {
|
function triggerGCByAllocation () {
|
||||||
|
@ -25,7 +46,7 @@ describe('WebContentsView', () => {
|
||||||
|
|
||||||
it('doesn\'t crash when GCed during allocation', (done) => {
|
it('doesn\'t crash when GCed during allocation', (done) => {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new WebContentsView({});
|
new WebContentsView();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// NB. the crash we're testing for is the lack of a current `v8::Context`
|
// NB. the crash we're testing for is the lack of a current `v8::Context`
|
||||||
// when emitting an event in WebContents's destructor. V8 is inconsistent
|
// when emitting an event in WebContents's destructor. V8 is inconsistent
|
||||||
|
@ -38,4 +59,94 @@ describe('WebContentsView', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('visibilityState', () => {
|
||||||
|
it('is initially hidden', async () => {
|
||||||
|
const v = new WebContentsView();
|
||||||
|
await v.webContents.loadURL('data:text/html,<script>initialVisibility = document.visibilityState</script>');
|
||||||
|
expect(await v.webContents.executeJavaScript('initialVisibility')).to.equal('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('becomes visibile when attached', async () => {
|
||||||
|
const v = new WebContentsView();
|
||||||
|
await v.webContents.loadURL('about:blank');
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('hidden');
|
||||||
|
const p = v.webContents.executeJavaScript('new Promise(resolve => document.addEventListener("visibilitychange", resolve))');
|
||||||
|
// Ensure that the above listener has been registered before we add the
|
||||||
|
// view to the window, or else the visibilitychange event might be
|
||||||
|
// dispatched before the listener is registered.
|
||||||
|
// executeJavaScript calls are sequential so if this one's finished then
|
||||||
|
// the previous one must also have been finished :)
|
||||||
|
await v.webContents.executeJavaScript('undefined');
|
||||||
|
const w = new BaseWindow();
|
||||||
|
w.setContentView(v);
|
||||||
|
await p;
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is initially visible if load happens after attach', async () => {
|
||||||
|
const w = new BaseWindow();
|
||||||
|
const v = new WebContentsView();
|
||||||
|
w.contentView = v;
|
||||||
|
await v.webContents.loadURL('data:text/html,<script>initialVisibility = document.visibilityState</script>');
|
||||||
|
expect(await v.webContents.executeJavaScript('initialVisibility')).to.equal('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('becomes hidden when parent window is hidden', async () => {
|
||||||
|
const w = new BaseWindow();
|
||||||
|
const v = new WebContentsView();
|
||||||
|
w.setContentView(v);
|
||||||
|
await v.webContents.loadURL('about:blank');
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('visible');
|
||||||
|
const p = v.webContents.executeJavaScript('new Promise(resolve => document.addEventListener("visibilitychange", resolve))');
|
||||||
|
// We have to wait until the listener above is fully registered before hiding the window.
|
||||||
|
// On Windows, the executeJavaScript and the visibilitychange can happen out of order
|
||||||
|
// without this.
|
||||||
|
await v.webContents.executeJavaScript('0');
|
||||||
|
w.hide();
|
||||||
|
await p;
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('becomes visible when parent window is shown', async () => {
|
||||||
|
const w = new BaseWindow({ show: false });
|
||||||
|
const v = new WebContentsView();
|
||||||
|
w.setContentView(v);
|
||||||
|
await v.webContents.loadURL('about:blank');
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('hidden');
|
||||||
|
const p = v.webContents.executeJavaScript('new Promise(resolve => document.addEventListener("visibilitychange", resolve))');
|
||||||
|
// We have to wait until the listener above is fully registered before hiding the window.
|
||||||
|
// On Windows, the executeJavaScript and the visibilitychange can happen out of order
|
||||||
|
// without this.
|
||||||
|
await v.webContents.executeJavaScript('0');
|
||||||
|
w.show();
|
||||||
|
await p;
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not change when view is moved between two visible windows', async () => {
|
||||||
|
const w = new BaseWindow();
|
||||||
|
const v = new WebContentsView();
|
||||||
|
w.setContentView(v);
|
||||||
|
await v.webContents.loadURL('about:blank');
|
||||||
|
expect(await v.webContents.executeJavaScript('document.visibilityState')).to.equal('visible');
|
||||||
|
|
||||||
|
const p = v.webContents.executeJavaScript('new Promise(resolve => document.addEventListener("visibilitychange", () => resolve(document.visibilityState)))');
|
||||||
|
// Ensure the listener has been registered.
|
||||||
|
await v.webContents.executeJavaScript('undefined');
|
||||||
|
const w2 = new BaseWindow();
|
||||||
|
w2.setContentView(v);
|
||||||
|
// Wait for the visibility state to settle as "visible".
|
||||||
|
// On macOS one visibilitychange event is fired but visibilityState
|
||||||
|
// remains "visible". On Win/Linux, two visibilitychange events are
|
||||||
|
// fired, a "hidden" and a "visible" one. Reconcile these two models
|
||||||
|
// by waiting until at least one event has been fired, and then waiting
|
||||||
|
// until the visibility state settles as "visible".
|
||||||
|
let visibilityState = await p;
|
||||||
|
for (let attempts = 0; visibilityState !== 'visible' && attempts < 10; attempts++) {
|
||||||
|
visibilityState = await v.webContents.executeJavaScript('new Promise(resolve => document.visibilityState === "visible" ? resolve("visible") : document.addEventListener("visibilitychange", () => resolve(document.visibilityState)))');
|
||||||
|
}
|
||||||
|
expect(visibilityState).to.equal('visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const { WebContentsView, app } = require('electron');
|
const { WebContentsView, app } = require('electron');
|
||||||
app.whenReady().then(function () {
|
app.whenReady().then(function () {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new WebContentsView({});
|
new WebContentsView();
|
||||||
|
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,32 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<button id="closeBtn">close webview</button>
|
||||||
<html lang="en">
|
<webview id="webview" src="webview.html"></webview>
|
||||||
|
<script>
|
||||||
<head>
|
webview.onclose = () => webview.remove()
|
||||||
<meta charset="UTF-8">
|
closeBtn.onclick = () => webview.executeJavaScript('window.close()', true)
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
</script>
|
||||||
<title>Document</title>
|
|
||||||
<style>
|
|
||||||
.webview {
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<button class="close-btn">close webview</button>
|
|
||||||
<webview class="webview" src="./webview.html"></webview>
|
|
||||||
<script>
|
|
||||||
const close = document.querySelector('.close-btn')
|
|
||||||
const webview = document.querySelector('.webview')
|
|
||||||
|
|
||||||
webview.addEventListener('close', () => {
|
|
||||||
webview.parentNode.removeChild(webview)
|
|
||||||
})
|
|
||||||
|
|
||||||
close.addEventListener('click', () => {
|
|
||||||
webview.executeJavaScript('window.close()', true)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ app.whenReady().then(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
contents.on('did-finish-load', () => {
|
contents.on('did-finish-load', () => {
|
||||||
win.webContents.executeJavaScript('document.querySelector(\'.close-btn\').click()');
|
win.webContents.executeJavaScript('closeBtn.click()');
|
||||||
});
|
});
|
||||||
|
|
||||||
contents.on('will-prevent-unload', event => {
|
contents.on('will-prevent-unload', event => {
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<h1>webview page</h1>
|
||||||
<html lang="en">
|
<script>
|
||||||
|
window.addEventListener('beforeunload', event => {
|
||||||
<head>
|
event.returnValue = 'test'
|
||||||
<meta charset="UTF-8">
|
})
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
</script>
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>webview page</h1>
|
|
||||||
<script>
|
|
||||||
window.addEventListener('beforeunload', event => {
|
|
||||||
event.returnValue = 'test'
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { BrowserWindow } from 'electron/main';
|
import { BaseWindow, BrowserWindow } from 'electron/main';
|
||||||
import { once } from 'node:events';
|
import { once } from 'node:events';
|
||||||
|
|
||||||
async function ensureWindowIsClosed (window: BrowserWindow | null) {
|
async function ensureWindowIsClosed (window: BaseWindow | null) {
|
||||||
if (window && !window.isDestroyed()) {
|
if (window && !window.isDestroyed()) {
|
||||||
if (window.webContents && !window.webContents.isDestroyed()) {
|
if (window instanceof BrowserWindow && window.webContents && !window.webContents.isDestroyed()) {
|
||||||
// If a window isn't destroyed already, and it has non-destroyed WebContents,
|
// If a window isn't destroyed already, and it has non-destroyed WebContents,
|
||||||
// then calling destroy() won't immediately destroy it, as it may have
|
// then calling destroy() won't immediately destroy it, as it may have
|
||||||
// <webview> children which need to be destroyed first. In that case, we
|
// <webview> children which need to be destroyed first. In that case, we
|
||||||
|
@ -23,13 +23,13 @@ async function ensureWindowIsClosed (window: BrowserWindow | null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const closeWindow = async (
|
export const closeWindow = async (
|
||||||
window: BrowserWindow | null = null,
|
window: BaseWindow | null = null,
|
||||||
{ assertNotWindows } = { assertNotWindows: true }
|
{ assertNotWindows } = { assertNotWindows: true }
|
||||||
) => {
|
) => {
|
||||||
await ensureWindowIsClosed(window);
|
await ensureWindowIsClosed(window);
|
||||||
|
|
||||||
if (assertNotWindows) {
|
if (assertNotWindows) {
|
||||||
const windows = BrowserWindow.getAllWindows();
|
const windows = BaseWindow.getAllWindows();
|
||||||
try {
|
try {
|
||||||
expect(windows).to.have.lengthOf(0);
|
expect(windows).to.have.lengthOf(0);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -41,7 +41,7 @@ export const closeWindow = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function closeAllWindows () {
|
export async function closeAllWindows () {
|
||||||
for (const w of BrowserWindow.getAllWindows()) {
|
for (const w of BaseWindow.getAllWindows()) {
|
||||||
await closeWindow(w, { assertNotWindows: false });
|
await closeWindow(w, { assertNotWindows: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as cp from 'node:child_process';
|
import * as cp from 'node:child_process';
|
||||||
import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron/main';
|
import { BaseWindow, BrowserWindow, BrowserWindowConstructorOptions, ipcMain, WebContents, WebContentsView } from 'electron/main';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
|
|
||||||
import { closeWindow } from './lib/window-helpers';
|
import { closeWindow } from './lib/window-helpers';
|
||||||
|
@ -11,16 +11,16 @@ import { setTimeout } from 'node:timers/promises';
|
||||||
// visibilityState specs pass on linux with a real window manager but on CI
|
// visibilityState specs pass on linux with a real window manager but on CI
|
||||||
// the environment does not let these specs pass
|
// the environment does not let these specs pass
|
||||||
ifdescribe(process.platform !== 'linux')('document.visibilityState', () => {
|
ifdescribe(process.platform !== 'linux')('document.visibilityState', () => {
|
||||||
let w: BrowserWindow;
|
let w: BaseWindow & {webContents: WebContents};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
return closeWindow(w);
|
return closeWindow(w);
|
||||||
});
|
});
|
||||||
|
|
||||||
const load = () => w.loadFile(path.resolve(__dirname, 'fixtures', 'chromium', 'visibilitystate.html'));
|
const load = () => w.webContents.loadFile(path.resolve(__dirname, 'fixtures', 'chromium', 'visibilitystate.html'));
|
||||||
|
|
||||||
const itWithOptions = (name: string, options: BrowserWindowConstructorOptions, fn: Mocha.Func) => {
|
const itWithOptions = (name: string, options: BrowserWindowConstructorOptions, fn: Mocha.Func) => {
|
||||||
return it(name, async function (...args) {
|
it(name, async function (...args) {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
...options,
|
...options,
|
||||||
paintWhenInitiallyHidden: false,
|
paintWhenInitiallyHidden: false,
|
||||||
|
@ -32,6 +32,16 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => {
|
||||||
});
|
});
|
||||||
await Promise.resolve(fn.apply(this, args));
|
await Promise.resolve(fn.apply(this, args));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(name + ' with BaseWindow', async function (...args) {
|
||||||
|
const baseWindow = new BaseWindow({
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
const wcv = new WebContentsView({ webPreferences: { ...(options.webPreferences ?? {}), nodeIntegration: true, contextIsolation: false } });
|
||||||
|
baseWindow.contentView = wcv;
|
||||||
|
w = Object.assign(baseWindow, { webContents: wcv.webContents });
|
||||||
|
await Promise.resolve(fn.apply(this, args));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
itWithOptions('should be visible when the window is initially shown by default', {}, async () => {
|
itWithOptions('should be visible when the window is initially shown by default', {}, async () => {
|
||||||
|
|
4
typings/internal-ambient.d.ts
vendored
4
typings/internal-ambient.d.ts
vendored
|
@ -1,7 +1,5 @@
|
||||||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||||
|
|
||||||
declare const ENABLE_VIEWS_API: boolean;
|
|
||||||
|
|
||||||
declare namespace NodeJS {
|
declare namespace NodeJS {
|
||||||
interface ModuleInternal extends NodeJS.Module {
|
interface ModuleInternal extends NodeJS.Module {
|
||||||
new(id: string, parent?: NodeJS.Module | null): NodeJS.Module;
|
new(id: string, parent?: NodeJS.Module | null): NodeJS.Module;
|
||||||
|
@ -18,7 +16,6 @@ declare namespace NodeJS {
|
||||||
isBuiltinSpellCheckerEnabled(): boolean;
|
isBuiltinSpellCheckerEnabled(): boolean;
|
||||||
isPDFViewerEnabled(): boolean;
|
isPDFViewerEnabled(): boolean;
|
||||||
isFakeLocationProviderEnabled(): boolean;
|
isFakeLocationProviderEnabled(): boolean;
|
||||||
isViewApiEnabled(): boolean;
|
|
||||||
isPrintingEnabled(): boolean;
|
isPrintingEnabled(): boolean;
|
||||||
isExtensionsEnabled(): boolean;
|
isExtensionsEnabled(): boolean;
|
||||||
isComponentBuild(): boolean;
|
isComponentBuild(): boolean;
|
||||||
|
@ -216,7 +213,6 @@ declare namespace NodeJS {
|
||||||
_linkedBinding(name: 'electron_common_v8_util'): V8UtilBinding;
|
_linkedBinding(name: 'electron_common_v8_util'): V8UtilBinding;
|
||||||
_linkedBinding(name: 'electron_browser_app'): { app: Electron.App, App: Function };
|
_linkedBinding(name: 'electron_browser_app'): { app: Electron.App, App: Function };
|
||||||
_linkedBinding(name: 'electron_browser_auto_updater'): { autoUpdater: Electron.AutoUpdater };
|
_linkedBinding(name: 'electron_browser_auto_updater'): { autoUpdater: Electron.AutoUpdater };
|
||||||
_linkedBinding(name: 'electron_browser_browser_view'): { BrowserView: typeof Electron.BrowserView };
|
|
||||||
_linkedBinding(name: 'electron_browser_crash_reporter'): CrashReporterBinding;
|
_linkedBinding(name: 'electron_browser_crash_reporter'): CrashReporterBinding;
|
||||||
_linkedBinding(name: 'electron_browser_desktop_capturer'): { createDesktopCapturer(): ElectronInternal.DesktopCapturer; };
|
_linkedBinding(name: 'electron_browser_desktop_capturer'): { createDesktopCapturer(): ElectronInternal.DesktopCapturer; };
|
||||||
_linkedBinding(name: 'electron_browser_event_emitter'): { setEventEmitterPrototype(prototype: Object): void; };
|
_linkedBinding(name: 'electron_browser_event_emitter'): { setEventEmitterPrototype(prototype: Object): void; };
|
||||||
|
|
29
typings/internal-electron.d.ts
vendored
29
typings/internal-electron.d.ts
vendored
|
@ -41,6 +41,13 @@ declare namespace Electron {
|
||||||
frameName: string;
|
frameName: string;
|
||||||
on(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
on(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
||||||
removeListener(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
removeListener(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
||||||
|
|
||||||
|
_browserViews: BrowserView[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BrowserView {
|
||||||
|
ownerWindow: BrowserWindow | null
|
||||||
|
webContentsView: WebContentsView
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BrowserWindowConstructorOptions {
|
interface BrowserWindowConstructorOptions {
|
||||||
|
@ -87,6 +94,7 @@ declare namespace Electron {
|
||||||
detachFromOuterFrame(): void;
|
detachFromOuterFrame(): void;
|
||||||
setEmbedder(embedder: Electron.WebContents): void;
|
setEmbedder(embedder: Electron.WebContents): void;
|
||||||
viewInstanceId: number;
|
viewInstanceId: number;
|
||||||
|
_setOwnerWindow(w: BaseWindow | null): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebFrameMain {
|
interface WebFrameMain {
|
||||||
|
@ -155,21 +163,6 @@ declare namespace Electron {
|
||||||
_replyChannel: ReplyChannel;
|
_replyChannel: ReplyChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
class View {}
|
|
||||||
|
|
||||||
// Experimental views API
|
|
||||||
class BaseWindow {
|
|
||||||
constructor(args: {show: boolean})
|
|
||||||
setContentView(view: View): void
|
|
||||||
static fromId(id: number): BaseWindow;
|
|
||||||
static getAllWindows(): BaseWindow[];
|
|
||||||
isFocused(): boolean;
|
|
||||||
static getFocusedWindow(): BaseWindow | undefined;
|
|
||||||
setMenu(menu: Menu): void;
|
|
||||||
}
|
|
||||||
class WebContentsView {
|
|
||||||
constructor(options: BrowserWindowConstructorOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated / undocumented BrowserWindow methods
|
// Deprecated / undocumented BrowserWindow methods
|
||||||
interface BrowserWindow {
|
interface BrowserWindow {
|
||||||
|
@ -191,12 +184,6 @@ declare namespace Electron {
|
||||||
registerProtocol(scheme: string, handler: any): boolean;
|
registerProtocol(scheme: string, handler: any): boolean;
|
||||||
interceptProtocol(scheme: string, handler: any): boolean;
|
interceptProtocol(scheme: string, handler: any): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Main {
|
|
||||||
class BaseWindow extends Electron.BaseWindow {}
|
|
||||||
class View extends Electron.View {}
|
|
||||||
class WebContentsView extends Electron.WebContentsView {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare namespace ElectronInternal {
|
declare namespace ElectronInternal {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue