mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
12 Commits
v13.0.0-ni
...
net-no-res
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325faae33c | ||
|
|
94f8b4b39a | ||
|
|
a75cd89d2a | ||
|
|
8bf66f8974 | ||
|
|
6edf6c6a95 | ||
|
|
79b3393768 | ||
|
|
c7aa35a519 | ||
|
|
357becd113 | ||
|
|
40aeb0d994 | ||
|
|
4f2490f8b8 | ||
|
|
18f4c3129d | ||
|
|
0bb1ba822a |
@@ -1 +1 @@
|
||||
13.0.0-nightly.20210128
|
||||
13.0.0-nightly.20210201
|
||||
@@ -28,15 +28,12 @@ The preferred method is to install Electron as a development dependency in your
|
||||
app:
|
||||
|
||||
```sh
|
||||
npm install electron --save-dev [--save-exact]
|
||||
npm install electron --save-dev
|
||||
```
|
||||
|
||||
The `--save-exact` flag is recommended for Electron prior to version 2, as it does not follow semantic
|
||||
versioning. As of version 2.0.0, Electron follows semver, so you don't need `--save-exact` flag. For info on how to manage Electron versions in your apps, see
|
||||
[Electron versioning](docs/tutorial/electron-versioning.md).
|
||||
|
||||
For more installation options and troubleshooting tips, see
|
||||
[installation](docs/tutorial/installation.md).
|
||||
[installation](docs/tutorial/installation.md). For info on how to manage Electron versions in your apps, see
|
||||
[Electron versioning](docs/tutorial/electron-versioning.md).
|
||||
|
||||
## Quick start & Electron Fiddle
|
||||
|
||||
|
||||
@@ -222,16 +222,14 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
the top left.
|
||||
* `hiddenInset` - Results in a hidden title bar with an alternative look
|
||||
where the traffic light buttons are slightly more inset from the window edge.
|
||||
* `customButtonsOnHover` Boolean (optional) - Draw custom close,
|
||||
and minimize buttons on macOS frameless windows. These buttons will not display
|
||||
unless hovered over in the top left of the window. These custom buttons prevent
|
||||
issues with mouse events that occur with the standard window toolbar buttons.
|
||||
**Note:** This option is currently experimental.
|
||||
* `customButtonsOnHover` - 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](structures/point.md) (optional) - Set a
|
||||
custom position for the traffic light buttons. Can only be used with
|
||||
`titleBarStyle` set to `hidden` or `customButtonsOnHover`.
|
||||
* `fullscreenWindowTitle` Boolean (optional) - Shows the title in the
|
||||
title bar in full screen mode on macOS for all `titleBarStyle` options.
|
||||
custom position for the traffic light buttons in frameless windows.
|
||||
* `fullscreenWindowTitle` Boolean (optional) _Deprecated_ - Shows the title in
|
||||
the title bar in full screen mode on macOS for `hiddenInset` titleBarStyle.
|
||||
Default is `false`.
|
||||
* `thickFrame` Boolean (optional) - Use `WS_THICKFRAME` style for frameless windows on
|
||||
Windows, which adds standard window frame. Setting it to `false` will remove
|
||||
@@ -1740,13 +1738,12 @@ deprecated and will be removed in an upcoming version of macOS.
|
||||
|
||||
* `position` [Point](structures/point.md)
|
||||
|
||||
Set a custom position for the traffic light buttons. Can only be used with
|
||||
`titleBarStyle` set to `hidden` or `customButtonsOnHover`.
|
||||
Set a custom position for the traffic light buttons in frameless window.
|
||||
|
||||
#### `win.getTrafficLightPosition()` _macOS_
|
||||
|
||||
Returns `Point` - The current position for the traffic light buttons. Can only
|
||||
be used with `titleBarStyle` set to `hidden` or `customButtonsOnHover`.
|
||||
Returns `Point` - The custom position for the traffic light buttons in
|
||||
frameless window.
|
||||
|
||||
#### `win.setTouchBar(touchBar)` _macOS_
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ $ pip install pyobjc
|
||||
If you're developing Electron and don't plan to redistribute your
|
||||
custom Electron build, you may skip this section.
|
||||
|
||||
Official Electron builds are built with [Xcode 9.4.1](http://adcdownload.apple.com/Developer_Tools/Xcode_9.4.1/Xcode_9.4.1.xip), and the macOS 10.13 SDK. Building with a newer SDK works too, but the releases currently use the 10.13 SDK.
|
||||
Official Electron builds are built with [Xcode 12.2](https://download.developer.apple.com/Developer_Tools/Xcode_12.2/Xcode_12.2.xip), and the macOS 11.0 SDK. Building with a newer SDK works too, but the releases currently use the 11.0 SDK.
|
||||
|
||||
## Building Electron
|
||||
|
||||
|
||||
@@ -180,6 +180,8 @@ filenames = {
|
||||
"shell/browser/ui/cocoa/root_view_mac.mm",
|
||||
"shell/browser/ui/cocoa/views_delegate_mac.h",
|
||||
"shell/browser/ui/cocoa/views_delegate_mac.mm",
|
||||
"shell/browser/ui/cocoa/window_buttons_view.h",
|
||||
"shell/browser/ui/cocoa/window_buttons_view.mm",
|
||||
"shell/browser/ui/drag_util_mac.mm",
|
||||
"shell/browser/ui/file_dialog_mac.mm",
|
||||
"shell/browser/ui/inspectable_web_contents_view_mac.h",
|
||||
|
||||
@@ -639,8 +639,10 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
|
||||
fs.promises.readdir = util.promisify(fs.readdir);
|
||||
|
||||
type ReaddirSyncOptions = { encoding: BufferEncoding | null; withFileTypes?: false };
|
||||
|
||||
const { readdirSync } = fs;
|
||||
fs.readdirSync = function (pathArgument: string, options: { encoding: BufferEncoding | null; withFileTypes?: false } | BufferEncoding | null) {
|
||||
fs.readdirSync = function (pathArgument: string, options: ReaddirSyncOptions | BufferEncoding | null) {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
if (!pathInfo.isAsar) return readdirSync.apply(this, arguments);
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
@@ -655,7 +657,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
}
|
||||
|
||||
if (options && (options as any).withFileTypes) {
|
||||
if (options && (options as ReaddirSyncOptions).withFileTypes) {
|
||||
const dirents = [];
|
||||
for (const file of files) {
|
||||
const childPath = path.join(filePath, file);
|
||||
|
||||
@@ -231,12 +231,12 @@ function sortTemplate (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
function generateGroupId (items: (MenuItemConstructorOptions | MenuItem)[], pos: number) {
|
||||
if (pos > 0) {
|
||||
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||
if (items[idx].type === 'radio') return (items[idx] as any).groupId;
|
||||
if (items[idx].type === 'radio') return (items[idx] as MenuItem).groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
} else if (pos < items.length) {
|
||||
for (let idx = pos; idx <= items.length - 1; idx++) {
|
||||
if (items[idx].type === 'radio') return (items[idx] as any).groupId;
|
||||
if (items[idx].type === 'radio') return (items[idx] as MenuItem).groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,9 +432,7 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
if (this._response) { this._response._storeInternalData(null, null); }
|
||||
});
|
||||
this._urlLoader.on('error', (event, netErrorString) => {
|
||||
const error = new Error(netErrorString);
|
||||
if (this._response) this._response.destroy(error);
|
||||
this._die(error);
|
||||
this._die(new Error(netErrorString));
|
||||
});
|
||||
|
||||
this._urlLoader.on('login', (event, authInfo, callback) => {
|
||||
@@ -515,7 +513,7 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
this.destroy(err);
|
||||
if (this._urlLoader) {
|
||||
this._urlLoader.cancel();
|
||||
if (this._response) this._response.destroy(err);
|
||||
if (this._response) this._response.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ const extendConstructHook = (target: any, hook: Function) => {
|
||||
};
|
||||
|
||||
const ImmutableProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never, setInternalProp: <K extends keyof T>(k: K, v: T[K]) => void) => any) => (target: T, propertyKey: keyof T) => {
|
||||
extendConstructHook(target as any, function (this: T) {
|
||||
extendConstructHook(target, function (this: T) {
|
||||
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config, (k, v) => {
|
||||
(this as any)[hiddenProperties][k] = v;
|
||||
});
|
||||
});
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function () {
|
||||
return (this as any)[hiddenProperties][propertyKey];
|
||||
return this[hiddenProperties][propertyKey];
|
||||
},
|
||||
set: function () {
|
||||
throw new Error(`Cannot override property ${name}`);
|
||||
@@ -31,7 +31,7 @@ const ImmutableProperty = <T extends TouchBarItem<any>>(def: (config: T extends
|
||||
};
|
||||
|
||||
const LiveProperty = <T extends TouchBarItem<any>>(def: (config: T extends TouchBarItem<infer C> ? C : never) => any, onMutate?: (self: T, newValue: any) => void) => (target: T, propertyKey: keyof T) => {
|
||||
extendConstructHook(target as any, function (this: T) {
|
||||
extendConstructHook(target, function (this: T) {
|
||||
(this as any)[hiddenProperties][propertyKey] = def((this as any)._config);
|
||||
if (onMutate) onMutate((this as any), (this as any)[hiddenProperties][propertyKey]);
|
||||
});
|
||||
@@ -59,7 +59,7 @@ abstract class TouchBarItem<ConfigType> extends EventEmitter {
|
||||
|
||||
constructor (config: ConfigType) {
|
||||
super();
|
||||
this._config = this._config || config || {} as any;
|
||||
this._config = this._config || config || {} as ConfigType;
|
||||
(this as any)[hiddenProperties] = {};
|
||||
const hook = (this as any)._hook;
|
||||
if (hook) hook.call(this);
|
||||
|
||||
@@ -22,18 +22,8 @@ const getNextId = function () {
|
||||
|
||||
type PostData = LoadURLOptions['postData']
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
type MediaSize = {
|
||||
name: string,
|
||||
custom_display_name: string,
|
||||
height_microns: number,
|
||||
width_microns: number,
|
||||
is_default?: 'true',
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
// Stock page sizes
|
||||
const PDFPageSizes: Record<string, MediaSize> = {
|
||||
const PDFPageSizes: Record<string, ElectronInternal.MediaSize> = {
|
||||
A5: {
|
||||
custom_display_name: 'A5',
|
||||
height_microns: 210000,
|
||||
@@ -90,7 +80,7 @@ const isValidCustomPageSize = (width: number, height: number) => {
|
||||
const defaultPrintingSetting = {
|
||||
// Customizable.
|
||||
pageRange: [] as {from: number, to: number}[],
|
||||
mediaSize: {} as MediaSize,
|
||||
mediaSize: {} as ElectronInternal.MediaSize,
|
||||
landscape: false,
|
||||
headerFooterEnabled: false,
|
||||
marginsType: 0,
|
||||
@@ -353,7 +343,7 @@ WebContents.prototype.printToPDF = async function (options) {
|
||||
}
|
||||
};
|
||||
|
||||
WebContents.prototype.print = function (options = {}, callback) {
|
||||
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions = {}, callback) {
|
||||
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
||||
// print param logic into new file shared between printToPDF and print
|
||||
if (typeof options === 'object') {
|
||||
@@ -372,14 +362,14 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
|
||||
(options as any).mediaSize = {
|
||||
options.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
(options as any).mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
@@ -428,7 +418,7 @@ WebContents.prototype.setWindowOpenHandler = function (handler: (details: Electr
|
||||
this._windowOpenHandler = handler;
|
||||
};
|
||||
|
||||
WebContents.prototype._callWindowOpenHandler = function (event: any, url: string, frameName: string, rawFeatures: string): BrowserWindowConstructorOptions | null {
|
||||
WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event, url: string, frameName: string, rawFeatures: string): BrowserWindowConstructorOptions | null {
|
||||
if (!this._windowOpenHandler) {
|
||||
return null;
|
||||
}
|
||||
@@ -458,21 +448,21 @@ WebContents.prototype._callWindowOpenHandler = function (event: any, url: string
|
||||
}
|
||||
};
|
||||
|
||||
const addReplyToEvent = (event: any) => {
|
||||
const addReplyToEvent = (event: Electron.IpcMainEvent) => {
|
||||
const { processId, frameId } = event;
|
||||
event.reply = (...args: any[]) => {
|
||||
event.sender.sendToFrame([processId, frameId], ...args);
|
||||
event.reply = (channel: string, ...args: any[]) => {
|
||||
event.sender.sendToFrame([processId, frameId], channel, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
const addSenderFrameToEvent = (event: any) => {
|
||||
const addSenderFrameToEvent = (event: Electron.IpcMainEvent | Electron.IpcMainInvokeEvent) => {
|
||||
const { processId, frameId } = event;
|
||||
Object.defineProperty(event, 'senderFrame', {
|
||||
get: () => webFrameMain.fromId(processId, frameId)
|
||||
});
|
||||
};
|
||||
|
||||
const addReturnValueToEvent = (event: any) => {
|
||||
const addReturnValueToEvent = (event: Electron.IpcMainEvent) => {
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: (value) => event.sendReply(value),
|
||||
get: () => {}
|
||||
@@ -521,7 +511,7 @@ WebContents.prototype._init = function () {
|
||||
this.setMaxListeners(0);
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
if (internal) {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
@@ -532,7 +522,7 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-invoke' as any, function (event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-invoke' as any, function (event: Electron.IpcMainInvokeEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
event._reply = (result: any) => event.sendReply({ result });
|
||||
event._throw = (error: Error) => {
|
||||
@@ -547,7 +537,7 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-message-sync' as any, function (this: Electron.WebContents, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message-sync' as any, function (this: Electron.WebContents, event: Electron.IpcMainEvent, internal: boolean, channel: string, args: any[]) {
|
||||
addSenderFrameToEvent(event);
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
@@ -559,13 +549,13 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-ports' as any, function (event: any, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
this.on('-ipc-ports' as any, function (event: Electron.IpcMainEvent, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
event.ports = ports.map(p => new MessagePortMain(p));
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
// Handle context menu action request from pepper plugin.
|
||||
this.on('pepper-context-menu' as any, function (event: any, params: {x: number, y: number, menu: Array<(MenuItemConstructorOptions) | (MenuItem)>}, callback: () => void) {
|
||||
this.on('pepper-context-menu' as any, function (event: ElectronInternal.Event, params: {x: number, y: number, menu: Array<(MenuItemConstructorOptions) | (MenuItem)>}, callback: () => void) {
|
||||
// Access Menu via electron.Menu to prevent circular require.
|
||||
const menu = require('electron').Menu.buildFromTemplate(params.menu);
|
||||
menu.popup({
|
||||
@@ -596,8 +586,8 @@ WebContents.prototype._init = function () {
|
||||
|
||||
if (this.getType() !== 'remote') {
|
||||
// Make new windows requested by links behave like "window.open".
|
||||
this.on('-new-window' as any, (event: any, url: string, frameName: string, disposition: string,
|
||||
rawFeatures: string, referrer: any, postData: PostData) => {
|
||||
this.on('-new-window' as any, (event: ElectronInternal.Event, url: string, frameName: string, disposition: string,
|
||||
rawFeatures: string, referrer: Electron.Referrer, postData: PostData) => {
|
||||
openGuestWindow({
|
||||
event,
|
||||
embedder: event.sender,
|
||||
@@ -614,7 +604,7 @@ WebContents.prototype._init = function () {
|
||||
});
|
||||
|
||||
let windowOpenOverriddenOptions: BrowserWindowConstructorOptions | null = null;
|
||||
this.on('-will-add-new-contents' as any, (event: any, url: string, frameName: string, rawFeatures: string) => {
|
||||
this.on('-will-add-new-contents' as any, (event: ElectronInternal.Event, url: string, frameName: string, rawFeatures: string) => {
|
||||
windowOpenOverriddenOptions = this._callWindowOpenHandler(event, url, frameName, rawFeatures);
|
||||
if (!event.defaultPrevented) {
|
||||
const secureOverrideWebPreferences = windowOpenOverriddenOptions ? {
|
||||
@@ -632,7 +622,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||
this.on('-add-new-contents' as any, (event: any, webContents: Electron.WebContents, disposition: string,
|
||||
this.on('-add-new-contents' as any, (event: ElectronInternal.Event, webContents: Electron.WebContents, disposition: string,
|
||||
_userGesture: boolean, _left: number, _top: number, _width: number, _height: number, url: string, frameName: string,
|
||||
referrer: Electron.Referrer, rawFeatures: string, postData: PostData) => {
|
||||
const overriddenOptions = windowOpenOverriddenOptions || undefined;
|
||||
|
||||
@@ -197,7 +197,7 @@ const attachGuest = function (event: Electron.IpcMainInvokeEvent,
|
||||
// Inherit certain option values from embedder
|
||||
const lastWebPreferences = embedder.getLastWebPreferences();
|
||||
for (const [name, value] of inheritedWebPreferences) {
|
||||
if ((lastWebPreferences as any)[name] === value) {
|
||||
if (lastWebPreferences[name as keyof Electron.WebPreferences] === value) {
|
||||
(webPreferences as any)[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
embedder,
|
||||
features,
|
||||
frameName,
|
||||
isNativeWindowOpen,
|
||||
overrideOptions: overrideBrowserWindowOptions
|
||||
});
|
||||
|
||||
@@ -84,7 +85,7 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
httpReferrer: referrer,
|
||||
...(postData && {
|
||||
postData,
|
||||
extraHeaders: formatPostDataHeaders(postData)
|
||||
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -133,7 +134,7 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName }: {
|
||||
* `did-create-window` in 11.0.0. Will be removed in 12.0.0.
|
||||
*/
|
||||
function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs, browserWindowOptions, additionalFeatures, disposition, referrer, postData }: {
|
||||
event: { sender: WebContents, defaultPrevented: boolean },
|
||||
event: { sender: WebContents, defaultPrevented: boolean, newGuest?: BrowserWindow },
|
||||
embedder: WebContents,
|
||||
guest?: WebContents,
|
||||
windowOpenArgs: WindowOpenArgs,
|
||||
@@ -144,10 +145,10 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||
postData?: PostData,
|
||||
}): boolean {
|
||||
const { url, frameName } = windowOpenArgs;
|
||||
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && (embedder as any).getLastWebPreferences().disablePopups;
|
||||
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences().disablePopups;
|
||||
const postBody = postData ? {
|
||||
data: postData,
|
||||
headers: formatPostDataHeaders(postData)
|
||||
headers: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
||||
} : null;
|
||||
|
||||
embedder.emit(
|
||||
@@ -165,14 +166,14 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs,
|
||||
postBody
|
||||
);
|
||||
|
||||
const { newGuest } = event as any;
|
||||
const { newGuest } = event;
|
||||
if (isWebViewWithPopupsDisabled) return true;
|
||||
if (event.defaultPrevented) {
|
||||
if (newGuest) {
|
||||
if (guest === newGuest.webContents) {
|
||||
// The webContents is not changed, so set defaultPrevented to false to
|
||||
// stop the callers of this event from destroying the webContents.
|
||||
(event as any).defaultPrevented = false;
|
||||
event.defaultPrevented = false;
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({
|
||||
@@ -199,10 +200,11 @@ const securityWebPreferences: { [key: string]: boolean } = {
|
||||
enableWebSQL: false
|
||||
};
|
||||
|
||||
function makeBrowserWindowOptions ({ embedder, features, frameName, overrideOptions, useDeprecatedBehaviorForBareValues = true, useDeprecatedBehaviorForOptionInheritance = true }: {
|
||||
function makeBrowserWindowOptions ({ embedder, features, frameName, isNativeWindowOpen, overrideOptions, useDeprecatedBehaviorForBareValues = true, useDeprecatedBehaviorForOptionInheritance = true }: {
|
||||
embedder: WebContents,
|
||||
features: string,
|
||||
frameName: string,
|
||||
isNativeWindowOpen: boolean,
|
||||
overrideOptions?: BrowserWindowConstructorOptions,
|
||||
useDeprecatedBehaviorForBareValues?: boolean
|
||||
useDeprecatedBehaviorForOptionInheritance?: boolean
|
||||
@@ -216,13 +218,13 @@ function makeBrowserWindowOptions ({ embedder, features, frameName, overrideOpti
|
||||
options: {
|
||||
...(useDeprecatedBehaviorForOptionInheritance && deprecatedInheritedOptions),
|
||||
show: true,
|
||||
title: frameName,
|
||||
width: 800,
|
||||
height: 600,
|
||||
...(!isNativeWindowOpen && { title: frameName }),
|
||||
...parsedOptions,
|
||||
...overrideOptions,
|
||||
webPreferences: makeWebPreferences({ embedder, insecureParsedWebPreferences: parsedWebPreferences, secureOverrideWebPreferences: overrideOptions && overrideOptions.webPreferences, useDeprecatedBehaviorForOptionInheritance: true })
|
||||
}
|
||||
} as Electron.BrowserViewConstructorOptions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,13 +239,13 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
useDeprecatedBehaviorForOptionInheritance?: boolean
|
||||
}) {
|
||||
const deprecatedInheritedOptions = getDeprecatedInheritedOptions(embedder);
|
||||
const parentWebPreferences = (embedder as any).getLastWebPreferences();
|
||||
const securityWebPreferencesFromParent = Object.keys(securityWebPreferences).reduce((map, key) => {
|
||||
if (securityWebPreferences[key] === parentWebPreferences[key]) {
|
||||
map[key] = parentWebPreferences[key];
|
||||
const parentWebPreferences = embedder.getLastWebPreferences();
|
||||
const securityWebPreferencesFromParent = (Object.keys(securityWebPreferences).reduce((map, key) => {
|
||||
if (securityWebPreferences[key] === parentWebPreferences[key as keyof Electron.WebPreferences]) {
|
||||
(map as any)[key] = parentWebPreferences[key as keyof Electron.WebPreferences];
|
||||
}
|
||||
return map;
|
||||
}, {} as any);
|
||||
}, {} as Electron.WebPreferences));
|
||||
const openerId = parentWebPreferences.nativeWindowOpen ? null : embedder.id;
|
||||
|
||||
return {
|
||||
@@ -268,18 +270,18 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
* only critical security preferences will be inherited by default.
|
||||
*/
|
||||
function getDeprecatedInheritedOptions (embedder: WebContents) {
|
||||
if (!(embedder as any).browserWindowOptions) {
|
||||
if (!embedder.browserWindowOptions) {
|
||||
// If it's a webview, return just the webPreferences.
|
||||
return {
|
||||
webPreferences: (embedder as any).getLastWebPreferences()
|
||||
webPreferences: embedder.getLastWebPreferences()
|
||||
};
|
||||
}
|
||||
|
||||
const { type, show, ...inheritableOptions } = (embedder as any).browserWindowOptions;
|
||||
const { type, show, ...inheritableOptions } = embedder.browserWindowOptions;
|
||||
return inheritableOptions;
|
||||
}
|
||||
|
||||
function formatPostDataHeaders (postData: any) {
|
||||
function formatPostDataHeaders (postData: Electron.UploadRawData[]) {
|
||||
if (!postData) return;
|
||||
|
||||
let extraHeaders = 'content-type: application/x-www-form-urlencoded';
|
||||
|
||||
@@ -28,7 +28,7 @@ const getGuestWindow = function (guestContents: WebContents) {
|
||||
};
|
||||
|
||||
const isChildWindow = function (sender: WebContents, target: WebContents) {
|
||||
return (target as any).getLastWebPreferences().openerId === sender.id;
|
||||
return target.getLastWebPreferences().openerId === sender.id;
|
||||
};
|
||||
|
||||
const isRelatedWindow = function (sender: WebContents, target: WebContents) {
|
||||
@@ -43,7 +43,7 @@ const isScriptableWindow = function (sender: WebContents, target: WebContents) {
|
||||
};
|
||||
|
||||
const isNodeIntegrationEnabled = function (sender: WebContents) {
|
||||
return (sender as any).getLastWebPreferences().nodeIntegration === true;
|
||||
return sender.getLastWebPreferences().nodeIntegration === true;
|
||||
};
|
||||
|
||||
// Checks whether |sender| can access the |target|:
|
||||
@@ -65,15 +65,15 @@ ipcMainInternal.on(
|
||||
features: string
|
||||
) => {
|
||||
// This should only be allowed for senders that have nativeWindowOpen: false
|
||||
const lastWebPreferences = (event.sender as any).getLastWebPreferences();
|
||||
const lastWebPreferences = event.sender.getLastWebPreferences();
|
||||
if (lastWebPreferences.nativeWindowOpen || lastWebPreferences.sandbox) {
|
||||
(event as any).returnValue = null;
|
||||
event.returnValue = null;
|
||||
throw new Error(
|
||||
'GUEST_WINDOW_MANAGER_WINDOW_OPEN denied: expected native window.open'
|
||||
);
|
||||
}
|
||||
|
||||
const browserWindowOptions = (event.sender as any)._callWindowOpenHandler(event, url, frameName, features);
|
||||
const browserWindowOptions = event.sender._callWindowOpenHandler(event, url, frameName, features);
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ ipcMainInternal.on(
|
||||
embedder: event.sender,
|
||||
referrer: { url: '', policy: 'default' },
|
||||
disposition: 'new-window',
|
||||
overrideBrowserWindowOptions: browserWindowOptions,
|
||||
overrideBrowserWindowOptions: browserWindowOptions!,
|
||||
windowOpenArgs: {
|
||||
url: url || 'about:blank',
|
||||
frameName: frameName || '',
|
||||
@@ -90,7 +90,7 @@ ipcMainInternal.on(
|
||||
}
|
||||
});
|
||||
|
||||
(event as any).returnValue = guest ? guest.webContents.id : null;
|
||||
event.returnValue = guest ? guest.webContents.id : null;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...a
|
||||
if (remoteEvents.length > 0) {
|
||||
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
|
||||
remoteEvents.forEach((eventName) => {
|
||||
sender.removeListener(eventName as any, callIntoRenderer);
|
||||
sender.removeListener(eventName, callIntoRenderer);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -365,7 +365,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_REQUIRE, function (event, contextId, mo
|
||||
if (customEvent.defaultPrevented) {
|
||||
throw new Error(`Blocked remote.require('${moduleName}')`);
|
||||
} else {
|
||||
customEvent.returnValue = (process as any).mainModule.require(moduleName);
|
||||
customEvent.returnValue = process.mainModule.require(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const contextBridge: Electron.ContextBridge = {
|
||||
checkContextIsolationEnabled();
|
||||
return binding.exposeAPIInMainWorld(key, api);
|
||||
}
|
||||
} as any;
|
||||
};
|
||||
|
||||
export default contextBridge;
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ if (nodeIntegration) {
|
||||
// We do not want to add `uncaughtException` to our definitions
|
||||
// because we don't want anyone else (anywhere) to throw that kind
|
||||
// of error.
|
||||
global.process.emit('uncaughtException' as any, error as any);
|
||||
global.process.emit('uncaughtException', error as any);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -79,7 +79,7 @@ const isLocalhost = function () {
|
||||
*/
|
||||
const isUnsafeEvalEnabled: () => Promise<boolean> = function () {
|
||||
// Call _executeJavaScript to bypass the world-safe deprecation warning
|
||||
return (webFrame as any)._executeJavaScript(`(${(() => {
|
||||
return webFrame._executeJavaScript(`(${(() => {
|
||||
try {
|
||||
eval(window.trustedTypes.emptyScript); // eslint-disable-line no-eval
|
||||
} catch {
|
||||
|
||||
@@ -60,7 +60,7 @@ export class WebViewAttribute implements MutationHandler {
|
||||
}
|
||||
|
||||
// Called when the attribute's value changes.
|
||||
public handleMutation: MutationHandler['handleMutation'] = () => undefined as any
|
||||
public handleMutation: MutationHandler['handleMutation'] = () => undefined
|
||||
}
|
||||
|
||||
// An attribute that is treated as a Boolean.
|
||||
|
||||
@@ -94,7 +94,7 @@ const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeo
|
||||
// The customElements.define has to be called in a special scope.
|
||||
webViewImpl.webFrame.allowGuestViewElementDefinition(window, () => {
|
||||
window.customElements.define('webview', WebViewElement);
|
||||
(window as any).WebView = WebViewElement;
|
||||
window.WebView = WebViewElement;
|
||||
|
||||
// Delete the callbacks so developers cannot call them and produce unexpected
|
||||
// behavior.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "13.0.0-nightly.20210128",
|
||||
"version": "13.0.0-nightly.20210201",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -280,8 +280,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||
void NotifyWindowRotateGesture(float rotation);
|
||||
void NotifyWindowSheetBegin();
|
||||
void NotifyWindowSheetEnd();
|
||||
void NotifyWindowEnterFullScreen();
|
||||
void NotifyWindowLeaveFullScreen();
|
||||
virtual void NotifyWindowEnterFullScreen();
|
||||
virtual void NotifyWindowLeaveFullScreen();
|
||||
void NotifyWindowEnterHtmlFullScreen();
|
||||
void NotifyWindowLeaveHtmlFullScreen();
|
||||
void NotifyWindowAlwaysOnTopChanged();
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
@class ElectronNSWindowDelegate;
|
||||
@class ElectronPreviewItem;
|
||||
@class ElectronTouchBar;
|
||||
@class CustomWindowButtonView;
|
||||
@class WindowButtonsView;
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -139,6 +139,11 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
void CloseFilePreview() override;
|
||||
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
|
||||
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
|
||||
void NotifyWindowEnterFullScreen() override;
|
||||
void NotifyWindowLeaveFullScreen() override;
|
||||
|
||||
void NotifyWindowWillEnterFullScreen();
|
||||
void NotifyWindowWillLeaveFullScreen();
|
||||
|
||||
// Cleanup observers when window is getting closed. Note that the destructor
|
||||
// can be called much later after window gets closed, so we should not do
|
||||
@@ -153,8 +158,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
void SetCollectionBehavior(bool on, NSUInteger flag);
|
||||
void SetWindowLevel(int level);
|
||||
|
||||
void SetExitingFullScreen(bool flag);
|
||||
|
||||
enum class VisualEffectState {
|
||||
kFollowWindow,
|
||||
kActive,
|
||||
@@ -172,7 +175,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
ElectronPreviewItem* preview_item() const { return preview_item_.get(); }
|
||||
ElectronTouchBar* touch_bar() const { return touch_bar_.get(); }
|
||||
bool zoom_to_page_width() const { return zoom_to_page_width_; }
|
||||
bool fullscreen_window_title() const { return fullscreen_window_title_; }
|
||||
bool always_simple_fullscreen() const { return always_simple_fullscreen_; }
|
||||
bool exiting_fullscreen() const { return exiting_fullscreen_; }
|
||||
|
||||
@@ -186,9 +188,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
|
||||
private:
|
||||
// Add custom layers to the content view.
|
||||
void AddContentViewLayers(bool minimizable, bool closable);
|
||||
void AddContentViewLayers();
|
||||
|
||||
void InternalSetWindowButtonVisibility(bool visible);
|
||||
void InternalSetStandardButtonsVisibility(bool visible);
|
||||
void InternalSetParentWindow(NativeWindow* parent, bool attach);
|
||||
void SetForwardMouseMessages(bool forward);
|
||||
|
||||
@@ -197,7 +200,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
base::scoped_nsobject<ElectronNSWindowDelegate> window_delegate_;
|
||||
base::scoped_nsobject<ElectronPreviewItem> preview_item_;
|
||||
base::scoped_nsobject<ElectronTouchBar> touch_bar_;
|
||||
base::scoped_nsobject<CustomWindowButtonView> buttons_view_;
|
||||
base::scoped_nsobject<WindowButtonsView> buttons_view_;
|
||||
|
||||
// Event monitor for scroll wheel event.
|
||||
id wheel_event_monitor_;
|
||||
@@ -213,7 +216,6 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
|
||||
bool is_kiosk_ = false;
|
||||
bool was_fullscreen_ = false;
|
||||
bool zoom_to_page_width_ = false;
|
||||
bool fullscreen_window_title_ = false;
|
||||
bool resizable_ = true;
|
||||
bool exiting_fullscreen_ = false;
|
||||
base::Optional<gfx::Point> traffic_light_position_;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "shell/browser/ui/cocoa/electron_preview_item.h"
|
||||
#include "shell/browser/ui/cocoa/electron_touch_bar.h"
|
||||
#include "shell/browser/ui/cocoa/root_view_mac.h"
|
||||
#include "shell/browser/ui/cocoa/window_buttons_view.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents.h"
|
||||
#include "shell/browser/ui/inspectable_web_contents_view.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
@@ -119,106 +120,6 @@
|
||||
|
||||
@end
|
||||
|
||||
// Custom Quit, Minimize and Full Screen button container for frameless
|
||||
// windows.
|
||||
@interface CustomWindowButtonView : NSView {
|
||||
@private
|
||||
BOOL mouse_inside_;
|
||||
gfx::Point margin_;
|
||||
}
|
||||
|
||||
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin;
|
||||
- (void)setMargin:(const base::Optional<gfx::Point>&)margin;
|
||||
@end
|
||||
|
||||
@implementation CustomWindowButtonView
|
||||
|
||||
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin {
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
[self setMargin:margin];
|
||||
|
||||
NSButton* close_button =
|
||||
[NSWindow standardWindowButton:NSWindowCloseButton
|
||||
forStyleMask:NSWindowStyleMaskTitled];
|
||||
[close_button setTag:1];
|
||||
NSButton* miniaturize_button =
|
||||
[NSWindow standardWindowButton:NSWindowMiniaturizeButton
|
||||
forStyleMask:NSWindowStyleMaskTitled];
|
||||
[miniaturize_button setTag:2];
|
||||
|
||||
CGFloat x = 0;
|
||||
const CGFloat space_between = 20;
|
||||
|
||||
[close_button setFrameOrigin:NSMakePoint(x, 0)];
|
||||
x += space_between;
|
||||
[self addSubview:close_button];
|
||||
|
||||
[miniaturize_button setFrameOrigin:NSMakePoint(x, 0)];
|
||||
x += space_between;
|
||||
[self addSubview:miniaturize_button];
|
||||
|
||||
const auto last_button_frame = miniaturize_button.frame;
|
||||
[self setFrameSize:NSMakeSize(last_button_frame.origin.x +
|
||||
last_button_frame.size.width,
|
||||
last_button_frame.size.height)];
|
||||
|
||||
mouse_inside_ = NO;
|
||||
[self setNeedsDisplayForButtons];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setMargin:(const base::Optional<gfx::Point>&)margin {
|
||||
margin_ = margin.value_or(gfx::Point(7, 3));
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow {
|
||||
if (!self.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stay in upper left corner.
|
||||
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
|
||||
[self setFrameOrigin:NSMakePoint(margin_.x(), self.window.frame.size.height -
|
||||
self.frame.size.height -
|
||||
margin_.y())];
|
||||
}
|
||||
|
||||
- (BOOL)_mouseInGroup:(NSButton*)button {
|
||||
return mouse_inside_;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas {
|
||||
auto tracking_area = [[[NSTrackingArea alloc]
|
||||
initWithRect:NSZeroRect
|
||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
|
||||
NSTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil] autorelease];
|
||||
[self addTrackingArea:tracking_area];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event {
|
||||
[super mouseEntered:event];
|
||||
mouse_inside_ = YES;
|
||||
[self setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event {
|
||||
[super mouseExited:event];
|
||||
mouse_inside_ = NO;
|
||||
[self setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
- (void)setNeedsDisplayForButtons {
|
||||
for (NSView* subview in self.subviews) {
|
||||
[subview setHidden:!mouse_inside_];
|
||||
[subview setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ElectronProgressBar : NSProgressIndicator
|
||||
@end
|
||||
|
||||
@@ -370,11 +271,17 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
options.Get(options::kResizable, &resizable_);
|
||||
options.Get(options::kTitleBarStyle, &title_bar_style_);
|
||||
options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
|
||||
options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
|
||||
options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
|
||||
options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_);
|
||||
options.Get(options::kVisualEffectState, &visual_effect_state_);
|
||||
|
||||
if (options.Has(options::kFullscreenWindowTitle)) {
|
||||
EmitWarning(node::Environment::GetCurrent(v8::Isolate::GetCurrent()),
|
||||
"\"fullscreenWindowTitle\" option has been deprecated and is "
|
||||
"no-op now.",
|
||||
"electron");
|
||||
}
|
||||
|
||||
bool minimizable = true;
|
||||
options.Get(options::kMinimizable, &minimizable);
|
||||
|
||||
@@ -457,6 +364,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
[window_ setTitleVisibility:NSWindowTitleHidden];
|
||||
// Remove non-transparent corners, see http://git.io/vfonD.
|
||||
[window_ setOpaque:NO];
|
||||
// Hide the window buttons.
|
||||
InternalSetStandardButtonsVisibility(false);
|
||||
}
|
||||
|
||||
// Create a tab only if tabbing identifier is specified and window has
|
||||
@@ -471,14 +380,6 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the title bar.
|
||||
if (title_bar_style_ == TitleBarStyle::kHiddenInset) {
|
||||
base::scoped_nsobject<NSToolbar> toolbar(
|
||||
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
||||
[toolbar setShowsBaselineSeparator:NO];
|
||||
[window_ setToolbar:toolbar];
|
||||
}
|
||||
|
||||
// Resize to content bounds.
|
||||
bool use_content_size = false;
|
||||
options.Get(options::kUseContentSize, &use_content_size);
|
||||
@@ -527,7 +428,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
|
||||
// Default content view.
|
||||
SetContentView(new views::View());
|
||||
AddContentViewLayers(minimizable, closable);
|
||||
AddContentViewLayers();
|
||||
|
||||
original_frame_ = [window_ frame];
|
||||
original_level_ = [window_ level];
|
||||
@@ -830,6 +731,8 @@ bool NativeWindowMac::IsMovable() {
|
||||
|
||||
void NativeWindowMac::SetMinimizable(bool minimizable) {
|
||||
SetStyleMask(minimizable, NSMiniaturizableWindowMask);
|
||||
if (buttons_view_)
|
||||
[[buttons_view_ viewWithTag:1] setEnabled:minimizable];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMinimizable() {
|
||||
@@ -851,6 +754,8 @@ void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
|
||||
// On EL Capitan this flag is required to hide fullscreen button.
|
||||
SetCollectionBehavior(!fullscreenable,
|
||||
NSWindowCollectionBehaviorFullScreenAuxiliary);
|
||||
if (buttons_view_)
|
||||
[[buttons_view_ viewWithTag:2] setEnabled:fullscreenable];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsFullScreenable() {
|
||||
@@ -860,6 +765,8 @@ bool NativeWindowMac::IsFullScreenable() {
|
||||
|
||||
void NativeWindowMac::SetClosable(bool closable) {
|
||||
SetStyleMask(closable, NSWindowStyleMaskClosable);
|
||||
if (buttons_view_)
|
||||
[[buttons_view_ viewWithTag:0] setEnabled:closable];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsClosable() {
|
||||
@@ -940,9 +847,6 @@ void NativeWindowMac::Invalidate() {
|
||||
|
||||
void NativeWindowMac::SetTitle(const std::string& title) {
|
||||
[window_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
if (title_bar_style_ == TitleBarStyle::kHidden) {
|
||||
RedrawTrafficLights();
|
||||
}
|
||||
}
|
||||
|
||||
std::string NativeWindowMac::GetTitle() {
|
||||
@@ -1004,14 +908,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
window.level = NSPopUpMenuWindowLevel;
|
||||
}
|
||||
|
||||
if (!fullscreen_window_title()) {
|
||||
// Hide the titlebar
|
||||
SetStyleMask(false, NSWindowStyleMaskTitled);
|
||||
|
||||
// Resize the window to accommodate the _entire_ screen size
|
||||
fullscreenFrame.size.height -=
|
||||
[[[NSApplication sharedApplication] mainMenu] menuBarHeight];
|
||||
} else if (!window_button_visibility_.has_value()) {
|
||||
if (!window_button_visibility_.has_value()) {
|
||||
// Lets keep previous behaviour - hide window controls in titled
|
||||
// fullscreen mode when not specified otherwise.
|
||||
InternalSetWindowButtonVisibility(false);
|
||||
@@ -1027,11 +924,6 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
} else if (!simple_fullscreen && is_simple_fullscreen_) {
|
||||
is_simple_fullscreen_ = false;
|
||||
|
||||
if (!fullscreen_window_title()) {
|
||||
// Restore the titlebar
|
||||
SetStyleMask(true, NSWindowStyleMaskTitled);
|
||||
}
|
||||
|
||||
// Restore default window controls visibility state.
|
||||
if (!window_button_visibility_.has_value()) {
|
||||
bool visibility;
|
||||
@@ -1450,9 +1342,7 @@ bool NativeWindowMac::GetWindowButtonVisibility() const {
|
||||
void NativeWindowMac::SetTrafficLightPosition(
|
||||
base::Optional<gfx::Point> position) {
|
||||
traffic_light_position_ = std::move(position);
|
||||
if (title_bar_style_ == TitleBarStyle::kHidden) {
|
||||
RedrawTrafficLights();
|
||||
} else if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
|
||||
if (buttons_view_) {
|
||||
[buttons_view_ setMargin:position];
|
||||
[buttons_view_ viewDidMoveToWindow];
|
||||
}
|
||||
@@ -1463,64 +1353,8 @@ base::Optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
|
||||
}
|
||||
|
||||
void NativeWindowMac::RedrawTrafficLights() {
|
||||
// Ensure maximizable options retain pre-existing state.
|
||||
SetMaximizable(maximizable_);
|
||||
|
||||
// Changing system titlebar is only allowed for "hidden" titleBarStyle.
|
||||
if (!traffic_light_position_ || title_bar_style_ != TitleBarStyle::kHidden)
|
||||
return;
|
||||
|
||||
if (IsFullscreen())
|
||||
return;
|
||||
|
||||
NSWindow* window = window_;
|
||||
NSButton* close = [window standardWindowButton:NSWindowCloseButton];
|
||||
NSButton* miniaturize =
|
||||
[window standardWindowButton:NSWindowMiniaturizeButton];
|
||||
NSButton* zoom = [window standardWindowButton:NSWindowZoomButton];
|
||||
// Safety check just in case apple changes the view structure in a macOS
|
||||
// update
|
||||
DCHECK(close.superview);
|
||||
DCHECK(close.superview.superview);
|
||||
if (!close.superview || !close.superview.superview)
|
||||
return;
|
||||
NSView* titleBarContainerView = close.superview.superview;
|
||||
|
||||
// Hide the container when exiting fullscreen, otherwise traffic light buttons
|
||||
// jump
|
||||
if (exiting_fullscreen_) {
|
||||
[titleBarContainerView setHidden:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
[titleBarContainerView setHidden:NO];
|
||||
CGFloat buttonHeight = [close frame].size.height;
|
||||
CGFloat titleBarFrameHeight = buttonHeight + traffic_light_position_->y();
|
||||
CGRect titleBarRect = titleBarContainerView.frame;
|
||||
CGFloat titleBarWidth = NSWidth(titleBarRect);
|
||||
titleBarRect.size.height = titleBarFrameHeight;
|
||||
titleBarRect.origin.y = window.frame.size.height - titleBarFrameHeight;
|
||||
[titleBarContainerView setFrame:titleBarRect];
|
||||
|
||||
BOOL isRTL = [titleBarContainerView userInterfaceLayoutDirection] ==
|
||||
NSUserInterfaceLayoutDirectionRightToLeft;
|
||||
NSArray* windowButtons = @[ close, miniaturize, zoom ];
|
||||
const CGFloat space_between =
|
||||
[miniaturize frame].origin.x - [close frame].origin.x;
|
||||
for (NSUInteger i = 0; i < windowButtons.count; i++) {
|
||||
NSView* view = [windowButtons objectAtIndex:i];
|
||||
CGRect rect = [view frame];
|
||||
if (isRTL) {
|
||||
CGFloat buttonWidth = NSWidth(rect);
|
||||
// origin is always top-left, even in RTL
|
||||
rect.origin.x = titleBarWidth - traffic_light_position_->x() +
|
||||
(i * space_between) - buttonWidth;
|
||||
} else {
|
||||
rect.origin.x = traffic_light_position_->x() + (i * space_between);
|
||||
}
|
||||
rect.origin.y = (titleBarFrameHeight - rect.size.height) / 2;
|
||||
[view setFrameOrigin:rect.origin];
|
||||
}
|
||||
if (buttons_view_)
|
||||
[buttons_view_ setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetTouchBar(
|
||||
@@ -1642,6 +1476,41 @@ gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::NotifyWindowEnterFullScreen() {
|
||||
NativeWindow::NotifyWindowEnterFullScreen();
|
||||
// Restore the window title under fullscreen mode.
|
||||
if (buttons_view_)
|
||||
[window_ setTitleVisibility:NSWindowTitleVisible];
|
||||
RedrawTrafficLights();
|
||||
}
|
||||
|
||||
void NativeWindowMac::NotifyWindowLeaveFullScreen() {
|
||||
NativeWindow::NotifyWindowLeaveFullScreen();
|
||||
exiting_fullscreen_ = false;
|
||||
// Add back buttonsView after leaving fullscreen mode.
|
||||
if (buttons_view_) {
|
||||
InternalSetStandardButtonsVisibility(false);
|
||||
[[window_ contentView] addSubview:buttons_view_];
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::NotifyWindowWillEnterFullScreen() {
|
||||
// Remove the buttonsView otherwise window buttons won't show under
|
||||
// fullscreen mode.
|
||||
if (buttons_view_) {
|
||||
[buttons_view_ removeFromSuperview];
|
||||
InternalSetStandardButtonsVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
|
||||
// Hide window title after leaving fullscreen.
|
||||
if (buttons_view_)
|
||||
[window_ setTitleVisibility:NSWindowTitleHidden];
|
||||
exiting_fullscreen_ = true;
|
||||
RedrawTrafficLights();
|
||||
}
|
||||
|
||||
void NativeWindowMac::Cleanup() {
|
||||
DCHECK(!IsClosed());
|
||||
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
|
||||
@@ -1663,7 +1532,7 @@ void NativeWindowMac::OverrideNSWindowContentView() {
|
||||
[container_view_
|
||||
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[window_ setContentView:container_view_];
|
||||
AddContentViewLayers(IsMinimizable(), IsClosable());
|
||||
AddContentViewLayers();
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
|
||||
@@ -1692,10 +1561,6 @@ void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
|
||||
SetMaximizable(was_maximizable);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetExitingFullScreen(bool flag) {
|
||||
exiting_fullscreen_ = flag;
|
||||
}
|
||||
|
||||
bool NativeWindowMac::CanResize() const {
|
||||
return resizable_;
|
||||
}
|
||||
@@ -1710,7 +1575,7 @@ void NativeWindowMac::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
|
||||
base::BindOnce(&NativeWindow::RedrawTrafficLights, GetWeakPtr()));
|
||||
}
|
||||
|
||||
void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) {
|
||||
void NativeWindowMac::AddContentViewLayers() {
|
||||
// Make sure the bottom corner is rounded for non-modal windows:
|
||||
// http://crbug.com/396264.
|
||||
if (!is_modal()) {
|
||||
@@ -1742,46 +1607,39 @@ void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) {
|
||||
[[window_ contentView] viewDidMoveToWindow];
|
||||
}
|
||||
|
||||
// The fullscreen button should always be hidden for frameless window.
|
||||
[[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
|
||||
// Create a custom window buttons view.
|
||||
if (title_bar_style_ != TitleBarStyle::kNormal) {
|
||||
buttons_view_.reset(
|
||||
[[WindowButtonsView alloc] initWithMargin:traffic_light_position_]);
|
||||
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
|
||||
[buttons_view_ setShowOnHover:YES];
|
||||
if (title_bar_style_ == TitleBarStyle::kHiddenInset &&
|
||||
!traffic_light_position_)
|
||||
[buttons_view_ setMargin:gfx::Point(12, 11)];
|
||||
|
||||
// Create a custom window buttons view for kCustomButtonsOnHover.
|
||||
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
|
||||
buttons_view_.reset([[CustomWindowButtonView alloc]
|
||||
initWithMargin:traffic_light_position_]);
|
||||
|
||||
if (!minimizable)
|
||||
[[buttons_view_ viewWithTag:2] removeFromSuperview];
|
||||
if (!closable)
|
||||
[[buttons_view_ viewWithTag:1] removeFromSuperview];
|
||||
if (!IsClosable())
|
||||
[[buttons_view_ viewWithTag:0] setEnabled:NO];
|
||||
if (!IsMinimizable())
|
||||
[[buttons_view_ viewWithTag:1] setEnabled:NO];
|
||||
if (!IsFullScreenable())
|
||||
[[buttons_view_ viewWithTag:2] setEnabled:NO];
|
||||
|
||||
[[window_ contentView] addSubview:buttons_view_];
|
||||
}
|
||||
|
||||
// Hide the window buttons except for kHidden and kHiddenInset.
|
||||
if (title_bar_style_ == TitleBarStyle::kNormal ||
|
||||
title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
|
||||
[[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
|
||||
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES];
|
||||
|
||||
// Some third-party macOS utilities check the zoom button's enabled state
|
||||
// to determine whether to show custom UI on hover, so we disable it here
|
||||
// to prevent them from doing so in a frameless app window.
|
||||
SetMaximizable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::InternalSetWindowButtonVisibility(bool visible) {
|
||||
if (buttons_view_) {
|
||||
if (buttons_view_)
|
||||
[buttons_view_ setHidden:!visible];
|
||||
} else {
|
||||
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
|
||||
[[window_ standardWindowButton:NSWindowMiniaturizeButton]
|
||||
setHidden:!visible];
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
|
||||
}
|
||||
else
|
||||
InternalSetStandardButtonsVisibility(visible);
|
||||
}
|
||||
|
||||
void NativeWindowMac::InternalSetStandardButtonsVisibility(bool visible) {
|
||||
[[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
|
||||
[[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
|
||||
}
|
||||
|
||||
void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 13,0,0,20210128
|
||||
PRODUCTVERSION 13,0,0,20210128
|
||||
FILEVERSION 13,0,0,20210201
|
||||
PRODUCTVERSION 13,0,0,20210201
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -91,14 +91,17 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
|
||||
- (void)windowDidBecomeMain:(NSNotification*)notification {
|
||||
shell_->NotifyWindowFocus();
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidResignMain:(NSNotification*)notification {
|
||||
shell_->NotifyWindowBlur();
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification*)notification {
|
||||
shell_->NotifyWindowIsKeyChanged(true);
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification*)notification {
|
||||
@@ -110,6 +113,7 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
return;
|
||||
|
||||
shell_->NotifyWindowIsKeyChanged(false);
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
|
||||
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
|
||||
@@ -151,9 +155,6 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
- (void)windowDidResize:(NSNotification*)notification {
|
||||
[super windowDidResize:notification];
|
||||
shell_->NotifyWindowResize();
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillMove:(NSNotification*)notification {
|
||||
@@ -212,75 +213,23 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
|
||||
// Setting resizable to true before entering fullscreen
|
||||
shell_->NotifyWindowWillEnterFullScreen();
|
||||
// Setting resizable to true before entering fullscreen.
|
||||
is_resizable_ = shell_->IsResizable();
|
||||
shell_->SetResizable(true);
|
||||
// Hide the native toolbar before entering fullscreen, so there is no visual
|
||||
// artifacts.
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
|
||||
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
|
||||
[window setToolbar:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||
shell_->NotifyWindowEnterFullScreen();
|
||||
|
||||
// For frameless window we don't show set title for normal mode since the
|
||||
// titlebar is expected to be empty, but after entering fullscreen mode we
|
||||
// have to set one, because title bar is visible here.
|
||||
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
|
||||
if ((shell_->transparent() || !shell_->has_frame()) &&
|
||||
// FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under
|
||||
// fullscreen mode.
|
||||
// Show title if fullscreen_window_title flag is set
|
||||
(shell_->title_bar_style() != TitleBarStyle::kHiddenInset ||
|
||||
shell_->fullscreen_window_title())) {
|
||||
[window setTitleVisibility:NSWindowTitleVisible];
|
||||
}
|
||||
|
||||
// Restore the native toolbar immediately after entering fullscreen, if we
|
||||
// do this before leaving fullscreen, traffic light buttons will be jumping.
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
|
||||
base::scoped_nsobject<NSToolbar> toolbar(
|
||||
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
||||
[toolbar setShowsBaselineSeparator:NO];
|
||||
[window setToolbar:toolbar];
|
||||
|
||||
// Set window style to hide the toolbar, otherwise the toolbar will show
|
||||
// in fullscreen mode.
|
||||
[window setTitlebarAppearsTransparent:NO];
|
||||
shell_->SetStyleMask(true, NSWindowStyleMaskFullSizeContentView);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
||||
// Restore the titlebar visibility.
|
||||
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
|
||||
if ((shell_->transparent() || !shell_->has_frame()) &&
|
||||
(shell_->title_bar_style() != TitleBarStyle::kHiddenInset ||
|
||||
shell_->fullscreen_window_title())) {
|
||||
[window setTitleVisibility:NSWindowTitleHidden];
|
||||
}
|
||||
|
||||
// Turn off the style for toolbar.
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHiddenInset) {
|
||||
shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView);
|
||||
[window setTitlebarAppearsTransparent:YES];
|
||||
}
|
||||
shell_->SetExitingFullScreen(true);
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
shell_->NotifyWindowWillLeaveFullScreen();
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
||||
shell_->SetResizable(is_resizable_);
|
||||
shell_->NotifyWindowLeaveFullScreen();
|
||||
shell_->SetExitingFullScreen(false);
|
||||
if (shell_->title_bar_style() == TitleBarStyle::kHidden) {
|
||||
shell_->RedrawTrafficLights();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)notification {
|
||||
|
||||
31
shell/browser/ui/cocoa/window_buttons_view.h
Normal file
31
shell/browser/ui/cocoa/window_buttons_view.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2021 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
|
||||
#define SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/optional.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
|
||||
// Custom Quit, Minimize and Full Screen button container for frameless
|
||||
// windows.
|
||||
@interface WindowButtonsView : NSView {
|
||||
@private
|
||||
BOOL mouse_inside_;
|
||||
BOOL show_on_hover_;
|
||||
BOOL is_rtl_;
|
||||
gfx::Point margin_;
|
||||
base::scoped_nsobject<NSTrackingArea> tracking_area_;
|
||||
}
|
||||
|
||||
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin;
|
||||
- (void)setMargin:(const base::Optional<gfx::Point>&)margin;
|
||||
- (void)setShowOnHover:(BOOL)yes;
|
||||
- (void)setNeedsDisplayForButtons;
|
||||
@end
|
||||
|
||||
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_VIEW_H_
|
||||
119
shell/browser/ui/cocoa/window_buttons_view.mm
Normal file
119
shell/browser/ui/cocoa/window_buttons_view.mm
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2021 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/cocoa/window_buttons_view.h"
|
||||
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "ui/gfx/mac/coordinate_conversion.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const CGFloat kButtonPadding = 20.;
|
||||
|
||||
const NSWindowButton kButtonTypes[] = {
|
||||
NSWindowCloseButton,
|
||||
NSWindowMiniaturizeButton,
|
||||
NSWindowZoomButton,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@implementation WindowButtonsView
|
||||
|
||||
- (id)initWithMargin:(const base::Optional<gfx::Point>&)margin {
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
[self setMargin:margin];
|
||||
|
||||
mouse_inside_ = false;
|
||||
show_on_hover_ = false;
|
||||
is_rtl_ = base::i18n::IsRTL();
|
||||
|
||||
for (size_t i = 0; i < base::size(kButtonTypes); ++i) {
|
||||
NSButton* button = [NSWindow standardWindowButton:kButtonTypes[i]
|
||||
forStyleMask:NSWindowStyleMaskTitled];
|
||||
[button setTag:i];
|
||||
int left_index = is_rtl_ ? base::size(kButtonTypes) - i - 1 : i;
|
||||
[button setFrameOrigin:NSMakePoint(left_index * kButtonPadding, 0)];
|
||||
[self addSubview:button];
|
||||
}
|
||||
|
||||
NSView* last_button =
|
||||
is_rtl_ ? [[self subviews] firstObject] : [[self subviews] lastObject];
|
||||
[self setFrameSize:NSMakeSize(last_button.frame.origin.x +
|
||||
last_button.frame.size.width,
|
||||
last_button.frame.size.height)];
|
||||
[self setNeedsDisplayForButtons];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setMargin:(const base::Optional<gfx::Point>&)margin {
|
||||
margin_ = margin.value_or(gfx::Point(7, 3));
|
||||
}
|
||||
|
||||
- (void)setShowOnHover:(BOOL)yes {
|
||||
show_on_hover_ = yes;
|
||||
[self setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
- (void)setNeedsDisplayForButtons {
|
||||
for (NSView* subview in self.subviews) {
|
||||
[subview setHidden:(show_on_hover_ && !mouse_inside_)];
|
||||
[subview setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview {
|
||||
[super removeFromSuperview];
|
||||
mouse_inside_ = NO;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow {
|
||||
// Stay in upper left corner.
|
||||
CGFloat y =
|
||||
self.superview.frame.size.height - self.frame.size.height - margin_.y();
|
||||
if (is_rtl_) {
|
||||
CGFloat x =
|
||||
self.superview.frame.size.width - self.frame.size.width - margin_.x();
|
||||
[self setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
|
||||
[self setFrameOrigin:NSMakePoint(x, y)];
|
||||
} else {
|
||||
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
|
||||
[self setFrameOrigin:NSMakePoint(margin_.x(), y)];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_mouseInGroup:(NSButton*)button {
|
||||
return mouse_inside_;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas {
|
||||
[super updateTrackingAreas];
|
||||
if (tracking_area_)
|
||||
[self removeTrackingArea:tracking_area_.get()];
|
||||
|
||||
tracking_area_.reset([[NSTrackingArea alloc]
|
||||
initWithRect:NSZeroRect
|
||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
|
||||
NSTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil]);
|
||||
[self addTrackingArea:tracking_area_.get()];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event {
|
||||
[super mouseEntered:event];
|
||||
mouse_inside_ = YES;
|
||||
[self setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event {
|
||||
[super mouseExited:event];
|
||||
mouse_inside_ = NO;
|
||||
[self setNeedsDisplayForButtons];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -167,7 +167,7 @@ bool MenuBar::AcceleratorPressed(const ui::Accelerator& accelerator) {
|
||||
|
||||
if (keycode == accelerator.key_code()) {
|
||||
auto event = accelerator.ToKeyEvent();
|
||||
ButtonPressed(button, event);
|
||||
ButtonPressed(button->tag(), event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ const char* MenuBar::GetClassName() const {
|
||||
return kViewClassName;
|
||||
}
|
||||
|
||||
void MenuBar::ButtonPressed(views::Button* source, const ui::Event& event) {
|
||||
void MenuBar::ButtonPressed(int id, const ui::Event& event) {
|
||||
// Hide the accelerator when a submenu is activated.
|
||||
SetAcceleratorVisibility(false);
|
||||
|
||||
@@ -264,13 +264,22 @@ void MenuBar::ButtonPressed(views::Button* source, const ui::Event& event) {
|
||||
if (!window_->HasFocus())
|
||||
window_->RequestFocus();
|
||||
|
||||
int id = source->tag();
|
||||
ElectronMenuModel::ItemType type = menu_model_->GetTypeAt(id);
|
||||
if (type != ElectronMenuModel::TYPE_SUBMENU) {
|
||||
menu_model_->ActivatedAt(id, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
SubmenuButton* source = nullptr;
|
||||
for (auto* child : children()) {
|
||||
auto* button = static_cast<SubmenuButton*>(child);
|
||||
if (button->tag() == id) {
|
||||
source = button;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DCHECK(source);
|
||||
|
||||
// Deleted in MenuDelegate::OnMenuClosed
|
||||
auto* menu_delegate = new MenuDelegate(this);
|
||||
menu_delegate->RunMenu(
|
||||
@@ -304,12 +313,10 @@ void MenuBar::OnThemeChanged() {
|
||||
void MenuBar::RebuildChildren() {
|
||||
RemoveAllChildViews(true);
|
||||
for (int i = 0, n = GetItemCount(); i < n; ++i) {
|
||||
auto* button =
|
||||
new SubmenuButton(menu_model_->GetLabelAt(i), background_color_);
|
||||
auto* button = new SubmenuButton(
|
||||
base::BindRepeating(&MenuBar::ButtonPressed, base::Unretained(this), i),
|
||||
menu_model_->GetLabelAt(i), background_color_);
|
||||
button->set_tag(i);
|
||||
button->SetCallback(base::BindRepeating(&MenuBar::ButtonPressed,
|
||||
base::Unretained(this),
|
||||
base::Unretained(button)));
|
||||
AddChildView(button);
|
||||
}
|
||||
UpdateViewColors();
|
||||
|
||||
@@ -80,7 +80,7 @@ class MenuBar : public views::AccessiblePaneView,
|
||||
// views::View:
|
||||
const char* GetClassName() const override;
|
||||
|
||||
void ButtonPressed(views::Button* source, const ui::Event& event);
|
||||
void ButtonPressed(int id, const ui::Event& event);
|
||||
|
||||
void RebuildChildren();
|
||||
void UpdateViewColors();
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
SubmenuButton::SubmenuButton(const base::string16& title,
|
||||
SubmenuButton::SubmenuButton(PressedCallback callback,
|
||||
const base::string16& title,
|
||||
const SkColor& background_color)
|
||||
: views::MenuButton(PressedCallback(), gfx::RemoveAccelerator(title)),
|
||||
: views::MenuButton(callback, gfx::RemoveAccelerator(title)),
|
||||
background_color_(background_color) {
|
||||
#if defined(OS_LINUX)
|
||||
// Dont' use native style border.
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace electron {
|
||||
// Special button that used by menu bar to show submenus.
|
||||
class SubmenuButton : public views::MenuButton {
|
||||
public:
|
||||
SubmenuButton(const base::string16& title, const SkColor& background_color);
|
||||
SubmenuButton(PressedCallback callback,
|
||||
const base::string16& title,
|
||||
const SkColor& background_color);
|
||||
~SubmenuButton() override;
|
||||
|
||||
void SetAcceleratorVisibility(bool visible);
|
||||
|
||||
@@ -96,7 +96,8 @@ bool IsPlainObject(const v8::Local<v8::Value>& object) {
|
||||
object->IsArrayBuffer() || object->IsArrayBufferView() ||
|
||||
object->IsArray() || object->IsDataView() ||
|
||||
object->IsSharedArrayBuffer() || object->IsProxy() ||
|
||||
object->IsWasmModuleObject() || object->IsModuleNamespaceObject());
|
||||
object->IsWasmModuleObject() || object->IsWasmMemoryObject() ||
|
||||
object->IsModuleNamespaceObject());
|
||||
}
|
||||
|
||||
bool IsPlainArray(const v8::Local<v8::Value>& arr) {
|
||||
|
||||
@@ -981,7 +981,7 @@ describe('contextBridge', () => {
|
||||
describe('overrideGlobalValueFromIsolatedWorld', () => {
|
||||
it('should override top level properties', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalValueFromIsolatedWorld(['open'], () => ({ you: 'are a wizard' }));
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld(['open'], () => ({ you: 'are a wizard' }));
|
||||
});
|
||||
const result = await callWithBindings(async (root: any) => {
|
||||
return root.open();
|
||||
@@ -991,7 +991,7 @@ describe('contextBridge', () => {
|
||||
|
||||
it('should override deep properties', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalValueFromIsolatedWorld(['document', 'foo'], () => 'I am foo');
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld(['document', 'foo'], () => 'I am foo');
|
||||
});
|
||||
const result = await callWithBindings(async (root: any) => {
|
||||
return root.document.foo();
|
||||
@@ -1008,7 +1008,7 @@ describe('contextBridge', () => {
|
||||
callCount++;
|
||||
return true;
|
||||
};
|
||||
contextBridge.internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['isFun'], getter);
|
||||
contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], getter);
|
||||
contextBridge.exposeInMainWorld('foo', {
|
||||
callCount: () => callCount
|
||||
});
|
||||
@@ -1022,7 +1022,7 @@ describe('contextBridge', () => {
|
||||
|
||||
it('should not make a setter if none is provided', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true);
|
||||
contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true);
|
||||
});
|
||||
const result = await callWithBindings(async (root: any) => {
|
||||
root.isFun = 123;
|
||||
@@ -1038,7 +1038,7 @@ describe('contextBridge', () => {
|
||||
callArgs.push(args);
|
||||
return true;
|
||||
};
|
||||
contextBridge.internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true, setter);
|
||||
contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true, setter);
|
||||
contextBridge.exposeInMainWorld('foo', {
|
||||
callArgs: () => callArgs
|
||||
});
|
||||
@@ -1056,7 +1056,7 @@ describe('contextBridge', () => {
|
||||
describe('overrideGlobalValueWithDynamicPropsFromIsolatedWorld', () => {
|
||||
it('should not affect normal values', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
a: 123,
|
||||
b: () => 2,
|
||||
c: () => ({ d: 3 })
|
||||
@@ -1070,7 +1070,7 @@ describe('contextBridge', () => {
|
||||
|
||||
it('should work with getters', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
get foo () {
|
||||
return 'hi there';
|
||||
}
|
||||
@@ -1085,7 +1085,7 @@ describe('contextBridge', () => {
|
||||
it('should work with setters', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
let a: any = null;
|
||||
contextBridge.internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
get foo () {
|
||||
return a;
|
||||
},
|
||||
@@ -1103,7 +1103,7 @@ describe('contextBridge', () => {
|
||||
|
||||
it('should work with deep properties', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
contextBridge.internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], {
|
||||
a: () => ({
|
||||
get foo () {
|
||||
return 'still here';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { expect } from 'chai';
|
||||
import { net, session, ClientRequest, BrowserWindow, ClientRequestConstructorOptions } from 'electron/main';
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import { AddressInfo, Socket } from 'net';
|
||||
import { AddressInfo, Socket, createServer } from 'net';
|
||||
import { emittedOnce } from './events-helpers';
|
||||
import { defer, delay } from './spec-helpers';
|
||||
|
||||
@@ -1536,6 +1536,34 @@ describe('net module', () => {
|
||||
expect(response.statusCode).to.equal(200);
|
||||
await collectStreamBody(response);
|
||||
});
|
||||
|
||||
it('should not trigger errors on the response stream', async () => {
|
||||
const ses = session.fromPartition(`${Math.random()}`);
|
||||
const serverPort = await new Promise(resolve => {
|
||||
const server = createServer((c) => {
|
||||
c.end('HTTP/1.1 407 Authentication Required\nProxy-Authenticate: Basic realm="Foo"\n\n');
|
||||
}).listen(0, '127.0.0.1', async () => {
|
||||
resolve((server.address() as AddressInfo).port);
|
||||
});
|
||||
});
|
||||
await ses.setProxy({ proxyRules: `127.0.0.1:${serverPort}` });
|
||||
const error = await new Promise<Error>((resolve, reject) => {
|
||||
net
|
||||
.request({ method: 'GET', url: 'https://example.com', session: ses })
|
||||
.once('response', (res) => {
|
||||
res.on('error', () => {
|
||||
reject(new Error('response stream should not emit error'));
|
||||
});
|
||||
})
|
||||
.once('abort', () => reject(new Error('should not abort')))
|
||||
.once('error', (err) => {
|
||||
resolve(err);
|
||||
})
|
||||
.once('login', (_, callback) => { callback('username', 'password'); })
|
||||
.end();
|
||||
});
|
||||
expect(error.message).to.equal('net::ERR_TUNNEL_CONNECTION_FAILED');
|
||||
});
|
||||
});
|
||||
|
||||
describe('IncomingMessage API', () => {
|
||||
|
||||
@@ -277,12 +277,12 @@ describe('chrome extensions', () => {
|
||||
it('can cancel http requests', async () => {
|
||||
await w.loadURL(url);
|
||||
await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest'));
|
||||
await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError);
|
||||
await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith('Failed to fetch');
|
||||
});
|
||||
|
||||
it('does not cancel http requests when no extension loaded', async () => {
|
||||
await w.loadURL(url);
|
||||
await expect(fetch(w.webContents, url)).to.not.be.rejectedWith(TypeError);
|
||||
await expect(fetch(w.webContents, url)).to.not.be.rejectedWith('Failed to fetch');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
"sender": "[WebContents]"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"width": 800,
|
||||
"title": "frame name",
|
||||
"title": "cool",
|
||||
"backgroundColor": "blue",
|
||||
"focusable": false,
|
||||
"webPreferences": {
|
||||
@@ -43,11 +43,11 @@
|
||||
"sender": "[WebContents]"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"width": 800,
|
||||
"title": "frame name",
|
||||
"title": "cool",
|
||||
"backgroundColor": "blue",
|
||||
"focusable": false,
|
||||
"webPreferences": {
|
||||
@@ -80,11 +80,11 @@
|
||||
"sender": "[WebContents]"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"width": 800,
|
||||
"title": "frame name",
|
||||
"title": "cool",
|
||||
"backgroundColor": "gray",
|
||||
"focusable": false,
|
||||
"webPreferences": {
|
||||
@@ -115,7 +115,7 @@
|
||||
"sender": "[WebContents]"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"width": 800,
|
||||
@@ -150,11 +150,11 @@
|
||||
"sender": "[WebContents]"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"width": 800,
|
||||
"title": "frame name",
|
||||
"title": "cool",
|
||||
"backgroundColor": "blue",
|
||||
"focusable": false,
|
||||
"webPreferences": {
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"title": "frame name",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "frame-name",
|
||||
"top": 5,
|
||||
"left": 10,
|
||||
"resizable": false,
|
||||
@@ -42,13 +42,13 @@
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"title": "frame name",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "frame-name",
|
||||
"resizable": false,
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
@@ -76,13 +76,13 @@
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"title": "frame name",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "frame-name",
|
||||
"backgroundColor": "gray",
|
||||
"webPreferences": {
|
||||
"nodeIntegration": false,
|
||||
@@ -110,13 +110,13 @@
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": true,
|
||||
"title": "sup",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "sup",
|
||||
"x": 50,
|
||||
"y": 20,
|
||||
"webPreferences": {
|
||||
@@ -142,13 +142,13 @@
|
||||
"processId": "placeholder-process-id"
|
||||
},
|
||||
"about:blank",
|
||||
"frame name",
|
||||
"frame-name",
|
||||
"new-window",
|
||||
{
|
||||
"show": false,
|
||||
"title": "frame name",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"title": "frame-name",
|
||||
"top": 1,
|
||||
"left": 1,
|
||||
"x": 1,
|
||||
|
||||
@@ -10,7 +10,7 @@ function genSnapshot (browserWindow: BrowserWindow, features: string) {
|
||||
browserWindow.webContents.on('new-window', (...args: any[]) => {
|
||||
resolve([features, ...args]);
|
||||
});
|
||||
browserWindow.webContents.executeJavaScript(`window.open('about:blank', 'frame name', '${features}') && true`);
|
||||
browserWindow.webContents.executeJavaScript(`window.open('about:blank', 'frame-name', '${features}') && true`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
2
typings/internal-ambient.d.ts
vendored
2
typings/internal-ambient.d.ts
vendored
@@ -232,6 +232,7 @@ declare namespace NodeJS {
|
||||
_firstFileName?: string;
|
||||
|
||||
helperExecPath: string;
|
||||
mainModule: NodeJS.Module;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +272,7 @@ declare interface Window {
|
||||
completeURL: (project: string, path: string) => string;
|
||||
}
|
||||
};
|
||||
WebView: typeof ElectronInternal.WebViewElement;
|
||||
ResizeObserver: ResizeObserver;
|
||||
trustedTypes: TrustedTypePolicyFactory;
|
||||
}
|
||||
|
||||
32
typings/internal-electron.d.ts
vendored
32
typings/internal-electron.d.ts
vendored
@@ -37,8 +37,12 @@ declare namespace Electron {
|
||||
removeListener(event: '-touch-bar-interaction', listener: (event: Event, itemID: string, details: any) => void): this;
|
||||
}
|
||||
|
||||
interface BrowserWindowConstructorOptions {
|
||||
webContents?: WebContents;
|
||||
}
|
||||
|
||||
interface ContextBridge {
|
||||
internalContextBridge: {
|
||||
internalContextBridge?: {
|
||||
contextIsolationEnabled: boolean;
|
||||
overrideGlobalValueFromIsolatedWorld(keys: string[], value: any): void;
|
||||
overrideGlobalValueWithDynamicPropsFromIsolatedWorld(keys: string[], value: any): void;
|
||||
@@ -88,6 +92,7 @@ declare namespace Electron {
|
||||
}
|
||||
|
||||
interface WebFrame {
|
||||
_executeJavaScript(code: string, userGesture?: boolean): Promise<any>;
|
||||
getWebFrameId(window: Window): number;
|
||||
allowGuestViewElementDefinition(window: Window, context: any): void;
|
||||
}
|
||||
@@ -100,7 +105,7 @@ declare namespace Electron {
|
||||
|
||||
interface WebPreferences {
|
||||
guestInstanceId?: number;
|
||||
openerId?: number;
|
||||
openerId?: number | null;
|
||||
disablePopups?: boolean;
|
||||
preloadURL?: string;
|
||||
embedder?: Electron.WebContents;
|
||||
@@ -147,9 +152,14 @@ declare namespace Electron {
|
||||
acceleratorWorksWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
interface IpcMainEvent {
|
||||
sendReply(value: any): void;
|
||||
}
|
||||
|
||||
interface IpcMainInvokeEvent {
|
||||
sendReply(value: any): void;
|
||||
_reply(value: any): void;
|
||||
_throw(error: Error): void;
|
||||
_throw(error: Error | string): void;
|
||||
}
|
||||
|
||||
const deprecate: ElectronInternal.DeprecationUtil;
|
||||
@@ -250,10 +260,26 @@ declare namespace ElectronInternal {
|
||||
once(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this;
|
||||
}
|
||||
|
||||
interface Event extends Electron.Event {
|
||||
sender: WebContents;
|
||||
}
|
||||
|
||||
interface LoadURLOptions extends Electron.LoadURLOptions {
|
||||
reloadIgnoringCache?: boolean;
|
||||
}
|
||||
|
||||
interface WebContentsPrintOptions extends Electron.WebContentsPrintOptions {
|
||||
mediaSize?: MediaSize;
|
||||
}
|
||||
|
||||
type MediaSize = {
|
||||
name: string,
|
||||
custom_display_name: string,
|
||||
height_microns: number,
|
||||
width_microns: number,
|
||||
is_default?: 'true',
|
||||
}
|
||||
|
||||
type ModuleLoader = () => any;
|
||||
|
||||
interface ModuleEntry {
|
||||
|
||||
Reference in New Issue
Block a user